summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp102
-rw-r--r--PREUPLOAD.cfg3
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java2
-rw-r--r--apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt18
-rw-r--r--apex/appsearch/framework/api/current.txt8
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java2
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java2
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java2
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl4
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java24
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java18
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java22
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java62
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java32
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java31
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java2
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/appsearch/testing/Android.bp5
-rw-r--r--apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java4
-rw-r--r--apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java77
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java33
-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.java408
-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.java38
-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/ApplicationMediaCapabilities.java10
-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--cmds/telecom/src/com/android/commands/telecom/Telecom.java13
-rw-r--r--core/api/current.txt411
-rw-r--r--core/api/module-lib-current.txt30
-rw-r--r--core/api/system-current.txt363
-rw-r--r--core/api/test-current.txt26
-rw-r--r--core/java/android/app/Activity.java24
-rw-r--r--core/java/android/app/ActivityThread.java56
-rw-r--r--core/java/android/app/AppOpsManager.java391
-rw-r--r--core/java/android/app/ContextImpl.java7
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/Instrumentation.java3
-rw-r--r--core/java/android/app/LocalActivityManager.java2
-rw-r--r--core/java/android/app/Notification.java282
-rw-r--r--core/java/android/app/OWNERS1
-rw-r--r--core/java/android/app/PendingIntent.java12
-rw-r--r--core/java/android/app/PictureInPictureParams.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java4
-rw-r--r--core/java/android/app/WallpaperColors.java46
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java7
-rw-r--r--core/java/android/app/assist/ActivityId.java125
-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/servertransaction/LaunchActivityItem.java22
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java50
-rw-r--r--core/java/android/app/usage/UsageEvents.java11
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java92
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java126
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java2
-rw-r--r--core/java/android/companion/AssociationRequest.java57
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java8
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/ContextParams.java121
-rw-r--r--core/java/android/content/OWNERS5
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java12
-rw-r--r--core/java/android/content/pm/IShortcutService.aidl58
-rw-r--r--core/java/android/content/pm/PackageManager.java31
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java11
-rw-r--r--core/java/android/content/pm/ShortcutManager.java108
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivity.java2
-rw-r--r--core/java/android/content/pm/permission/OWNERS8
-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/content/res/ApkAssets.java13
-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/camera2/CameraCharacteristics.java92
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java57
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java52
-rw-r--r--core/java/android/hardware/camera2/MultiResolutionImageReader.java309
-rw-r--r--core/java/android/hardware/camera2/TotalCaptureResult.java42
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java42
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java76
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java5
-rw-r--r--core/java/android/hardware/camera2/params/InputConfiguration.java82
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java17
-rw-r--r--core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java335
-rw-r--r--core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java113
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java126
-rw-r--r--core/java/android/hardware/camera2/params/SessionConfiguration.java6
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java22
-rw-r--r--core/java/android/hardware/display/ColorDisplayManager.java20
-rw-r--r--core/java/android/hardware/display/DeviceProductInfo.java99
-rw-r--r--core/java/android/hardware/lights/LightsRequest.java15
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java21
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl5
-rw-r--r--core/java/android/net/INetworkStatsService.aidl4
-rw-r--r--core/java/android/net/Ikev2VpnProfile.java42
-rw-r--r--core/java/android/net/IpSecAlgorithm.java26
-rw-r--r--core/java/android/net/NetworkIdentity.java44
-rw-r--r--core/java/android/net/NetworkStateSnapshot.java60
-rw-r--r--core/java/android/net/OemNetworkPreferences.java19
-rw-r--r--core/java/android/net/PacProxySelector.java (renamed from packages/Connectivity/framework/src/android/net/PacProxySelector.java)0
-rw-r--r--core/java/android/net/Proxy.java (renamed from packages/Connectivity/framework/src/android/net/Proxy.java)0
-rw-r--r--core/java/android/net/VpnService.java3
-rw-r--r--core/java/android/net/util/SocketUtils.java (renamed from packages/Connectivity/framework/src/android/net/util/SocketUtils.java)5
-rw-r--r--core/java/android/net/vcn/IVcnStatusCallback.aidl1
-rw-r--r--core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl2
-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/IkeTrafficSelectorUtils.java74
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java96
-rw-r--r--core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java285
-rw-r--r--core/java/android/os/BatterySaverPolicyConfig.java1
-rw-r--r--core/java/android/os/BatteryUsageStats.java49
-rwxr-xr-xcore/java/android/os/Build.java8
-rw-r--r--core/java/android/os/BytesMatcher.java248
-rw-r--r--core/java/android/os/CombinedVibrationEffect.java4
-rw-r--r--core/java/android/os/INetworkManagementService.aidl8
-rw-r--r--core/java/android/os/IPowerManager.aidl3
-rw-r--r--core/java/android/os/PowerManager.java2
-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/ZygoteProcess.java18
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java10
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl3
-rw-r--r--core/java/android/os/storage/StorageManager.java38
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java7
-rw-r--r--core/java/android/permission/OWNERS8
-rw-r--r--core/java/android/permission/PermissionControllerManager.java2
-rw-r--r--core/java/android/permissionpresenterservice/OWNERS6
-rw-r--r--core/java/android/provider/DeviceConfig.java16
-rw-r--r--core/java/android/provider/Settings.java24
-rw-r--r--core/java/android/provider/Telephony.java8
-rw-r--r--core/java/android/service/autofill/AutofillServiceInfo.java45
-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/telephony/PhoneStateListener.java1534
-rw-r--r--core/java/android/telephony/TelephonyCallback.java1710
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java291
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/OWNERS4
-rw-r--r--core/java/android/util/SizeF.java47
-rw-r--r--core/java/android/util/SparseArray.java7
-rw-r--r--core/java/android/util/apk/OWNERS1
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java354
-rw-r--r--core/java/android/view/AppTransitionAnimationSpec.java8
-rw-r--r--core/java/android/view/Choreographer.java5
-rw-r--r--core/java/android/view/CrossWindowBlurListeners.java137
-rw-r--r--core/java/android/view/Display.java13
-rw-r--r--core/java/android/view/DisplayInfo.java3
-rw-r--r--core/java/android/view/ICrossWindowBlurEnabledListener.aidl29
-rw-r--r--core/java/android/view/IWindowManager.aidl19
-rw-r--r--core/java/android/view/ImeFocusController.java5
-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/OWNERS1
-rw-r--r--core/java/android/view/RemoteAnimationDefinition.java4
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java44
-rw-r--r--core/java/android/view/View.java3
-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/Window.java11
-rw-r--r--core/java/android/view/WindowManager.java182
-rw-r--r--core/java/android/view/WindowManagerImpl.java16
-rw-r--r--core/java/android/view/WindowlessWindowManager.java19
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java93
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java6
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl9
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureContext.java46
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java5
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl12
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureOptionsCallback.aidl31
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java16
-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/view/translation/ITranslationManager.aidl6
-rw-r--r--core/java/android/view/translation/TranslationSpec.java18
-rw-r--r--core/java/android/view/translation/Translator.java7
-rw-r--r--core/java/android/view/translation/UiTranslationController.java74
-rw-r--r--core/java/android/view/translation/UiTranslationManager.java130
-rw-r--r--core/java/android/widget/EdgeEffect.java91
-rw-r--r--core/java/android/widget/HorizontalScrollView.java20
-rw-r--r--core/java/android/widget/RemoteViews.java117
-rw-r--r--core/java/android/widget/ScrollView.java20
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java26
-rw-r--r--core/java/android/window/StartingWindowInfo.java25
-rw-r--r--core/java/android/window/TaskSnapshot.java15
-rw-r--r--core/java/android/window/TransitionFilter.java4
-rw-r--r--core/java/android/window/TransitionInfo.java27
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl6
-rw-r--r--core/java/com/android/internal/graphics/palette/WuQuantizer.java7
-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/inputmethod/CallbackUtils.java27
-rw-r--r--core/java/com/android/internal/inputmethod/Completable.java13
-rw-r--r--core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl36
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java2
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java94
-rw-r--r--core/java/com/android/internal/inputmethod/ResultCallbacks.java37
-rw-r--r--core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java9
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java8
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java2
-rw-r--r--core/java/com/android/internal/net/NetworkUtilsInternal.java16
-rw-r--r--core/java/com/android/internal/os/BatteryChargeCalculator.java (renamed from core/java/com/android/internal/os/DischargedPowerCalculator.java)14
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java2
-rw-r--r--core/java/com/android/internal/os/OWNERS1
-rw-r--r--core/java/com/android/internal/os/Zygote.java20
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java6
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java2
-rw-r--r--core/java/com/android/internal/policy/DecorView.java81
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java15
-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/java/com/android/server/SystemConfig.java10
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/OWNERS1
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp17
-rw-r--r--core/jni/android_net_NetworkUtils.cpp (renamed from core/jni/android_net_NetUtils.cpp)13
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp123
-rw-r--r--core/jni/android_view_SurfaceControl.cpp23
-rw-r--r--core/jni/com_android_internal_net_NetworkUtilsInternal.cpp13
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp480
-rw-r--r--core/jni/fd_utils.cpp199
-rw-r--r--core/jni/fd_utils.h44
-rw-r--r--core/proto/OWNERS1
-rw-r--r--core/proto/android/server/activitymanagerservice.proto1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/res/AndroidManifest.xml20
-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.xml3
-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.xml44
-rw-r--r--core/res/res/values/dimens.xml30
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/strings.xml26
-rw-r--r--core/res/res/values/symbols.xml29
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java3
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java8
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java20
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java4
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java9
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java1
-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/BytesMatcherTest.java133
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java29
-rw-r--r--core/tests/coretests/src/android/security/CredentialManagementAppTest.java35
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java9
-rw-r--r--core/tests/coretests/src/android/view/accessibility/OWNERS1
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java15
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java (renamed from core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java)32
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java2
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java2
-rw-r--r--data/etc/car/Android.bp7
-rw-r--r--data/etc/car/android.car.cluster.xml2
-rw-r--r--data/etc/car/com.android.car.bugreport.xml1
-rw-r--r--data/etc/car/com.android.car.carlauncher.xml3
-rw-r--r--data/etc/car/com.android.car.dialer.xml1
-rw-r--r--data/etc/car/com.android.car.hvac.xml1
-rw-r--r--data/etc/car/com.android.car.radio.xml2
-rw-r--r--data/etc/car/com.android.car.rotary.xml23
-rw-r--r--data/etc/car/com.google.android.car.kitchensink.xml36
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--data/etc/platform.xml2
-rw-r--r--data/etc/privapp-permissions-platform.xml8
-rw-r--r--data/etc/services.core.protolog.json30
-rw-r--r--data/fonts/fonts.xml4
-rw-r--r--errorprone/Android.bp6
-rw-r--r--errorprone/TEST_MAPPING7
-rw-r--r--graphics/java/android/graphics/FontListParser.java14
-rw-r--r--graphics/java/android/graphics/RenderNode.java19
-rw-r--r--keystore/java/android/security/AppUriAuthenticationPolicy.java17
-rw-r--r--keystore/java/android/security/LegacyVpnProfileStore.java142
-rw-r--r--keystore/java/android/security/UrisToAliases.java18
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java35
-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/draganddrop/DragAndDropPolicy.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java196
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java35
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java4
-rwxr-xr-xlibs/androidfw/ApkAssets.cpp6
-rw-r--r--libs/androidfw/AssetManager2.cpp86
-rw-r--r--libs/androidfw/AssetsProvider.cpp29
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h12
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h2
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h10
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/FrameInfo.h4
-rw-r--r--libs/hwui/RenderProperties.h4
-rw-r--r--libs/hwui/effects/StretchEffect.cpp190
-rw-r--r--libs/hwui/effects/StretchEffect.h38
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp220
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp14
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp3
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp5
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl4
-rw-r--r--media/java/android/media/AudioMetadata.java55
-rw-r--r--media/java/android/media/AudioPresentation.java58
-rw-r--r--media/java/android/media/AudioTrack.java19
-rw-r--r--media/java/android/media/ImageReader.java120
-rw-r--r--media/java/android/media/metrics/Event.java15
-rw-r--r--media/java/android/media/metrics/IMediaMetricsManager.aidl3
-rw-r--r--media/java/android/media/metrics/LogSessionId.java34
-rw-r--r--media/java/android/media/metrics/MediaMetricsManager.java17
-rw-r--r--media/java/android/media/metrics/NetworkEvent.java36
-rw-r--r--media/java/android/media/metrics/PlaybackErrorEvent.java23
-rw-r--r--media/java/android/media/metrics/PlaybackMetrics.java65
-rw-r--r--media/java/android/media/metrics/PlaybackSession.java9
-rw-r--r--media/java/android/media/metrics/PlaybackStateEvent.java26
-rw-r--r--media/java/android/media/metrics/RecordingSession.java62
-rw-r--r--media/java/android/media/metrics/TrackChangeEvent.java114
-rw-r--r--media/jni/android_media_MediaCodecList.cpp1
-rw-r--r--media/jni/android_media_MediaDrm.cpp7
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java26
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/QosFilterParcelable.aidl (renamed from core/java/android/net/QosFilterParcelable.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/QosSession.aidl (renamed from core/java/android/net/QosSession.aidl)0
-rw-r--r--packages/Connectivity/framework/aidl-export/android/net/QosSocketInfo.aidl (renamed from core/java/android/net/QosSocketInfo.aidl)0
-rw-r--r--packages/Connectivity/framework/api/current.txt10
-rw-r--r--packages/Connectivity/framework/api/module-lib-current.txt5
-rw-r--r--packages/Connectivity/framework/api/system-current.txt65
-rw-r--r--packages/Connectivity/framework/src/android/net/CaptivePortal.java7
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java44
-rw-r--r--packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl1
-rw-r--r--packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl3
-rw-r--r--packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl (renamed from core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl)0
-rw-r--r--packages/Connectivity/framework/src/android/net/IQosCallback.aidl (renamed from core/java/android/net/IQosCallback.aidl)0
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkReleasedException.java (renamed from core/java/android/net/NetworkReleasedException.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkState.java (renamed from core/java/android/net/NetworkState.java)3
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkUtils.java16
-rw-r--r--packages/Connectivity/framework/src/android/net/QosCallback.java (renamed from core/java/android/net/QosCallback.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosCallbackConnection.java (renamed from core/java/android/net/QosCallbackConnection.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosCallbackException.java (renamed from core/java/android/net/QosCallbackException.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosFilter.java (renamed from core/java/android/net/QosFilter.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosFilterParcelable.java (renamed from core/java/android/net/QosFilterParcelable.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosSession.java (renamed from core/java/android/net/QosSession.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosSessionAttributes.java (renamed from core/java/android/net/QosSessionAttributes.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosSocketFilter.java (renamed from core/java/android/net/QosSocketFilter.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/QosSocketInfo.java (renamed from core/java/android/net/QosSocketInfo.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/SocketLocalAddressChangedException.java (renamed from core/java/android/net/SocketLocalAddressChangedException.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/SocketNotBoundException.java (renamed from core/java/android/net/SocketNotBoundException.java)0
-rw-r--r--packages/Connectivity/framework/src/android/net/UidRange.aidl (renamed from core/java/android/net/UidRange.aidl)0
-rw-r--r--packages/Connectivity/framework/src/android/net/UidRange.java (renamed from core/java/android/net/UidRange.java)31
-rw-r--r--packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java10
-rw-r--r--packages/Connectivity/service/Android.bp1
-rw-r--r--packages/Connectivity/service/jarjar-rules.txt3
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java24
-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.xml15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java50
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java31
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java4
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SoundPicker/res/layout-watch/add_new_sound_item.xml1
-rw-r--r--packages/SystemUI/README.md4
-rw-r--r--packages/SystemUI/docs/media-controls-pipeline.pngbin0 -> 84005 bytes
-rw-r--r--packages/SystemUI/docs/media-controls.md94
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml2
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values/config.xml1
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml4
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_settings_footer.xml1
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_bp.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view.xml)5
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_enroll.xml34
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml22
-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/colors.xml4
-rw-r--r--packages/SystemUI/res/values/config.xml7
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java226
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java68
-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.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java96
-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.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java327
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java38
-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/NotificationShelf.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java5
-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.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java59
-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/MobileSignalController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java152
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java216
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt146
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java25
-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/ActionReplacingCallback.java221
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java74
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java123
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java6
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java15
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java19
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java121
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java30
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java9
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/BootReceiver.java (renamed from core/java/com/android/server/BootReceiver.java)218
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java542
-rw-r--r--services/core/java/com/android/server/ConnectivityServiceInitializer.java16
-rw-r--r--services/core/java/com/android/server/IntentResolver.java23
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java36
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java113
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java292
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java10
-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/accounts/AccountManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java49
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java10
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java28
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java11
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java8
-rw-r--r--services/core/java/com/android/server/am/DataConnectionStats.java (renamed from services/core/java/com/android/server/connectivity/DataConnectionStats.java)26
-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/apphibernation/AppHibernationManagerInternal.java46
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java33
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java33
-rw-r--r--services/core/java/com/android/server/appop/DiscreteRegistry.java622
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java188
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java5
-rw-r--r--services/core/java/com/android/server/audio/FadeOutManager.java245
-rw-r--r--services/core/java/com/android/server/audio/FocusRequester.java52
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java81
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java106
-rw-r--r--services/core/java/com/android/server/audio/PlayerFocusEnforcer.java23
-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/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java2
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java42
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java87
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java35
-rw-r--r--services/core/java/com/android/server/connectivity/DnsManager.java22
-rw-r--r--services/core/java/com/android/server/connectivity/KeepaliveTracker.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java23
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java20
-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/devicestate/DeviceStateManagerService.java17
-rw-r--r--services/core/java/com/android/server/display/DeviceStateToLayoutMap.java88
-rw-r--r--services/core/java/com/android/server/display/DisplayGroup.java15
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java66
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java30
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java42
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java554
-rw-r--r--services/core/java/com/android/server/display/layout/Layout.java20
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java92
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java33
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java31
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java88
-rw-r--r--services/core/java/com/android/server/location/GeocoderProxy.java2
-rw-r--r--services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java4
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceProxy.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java10
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java7
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java18
-rw-r--r--services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java18
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java3
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java6
-rw-r--r--services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java13
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java9
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java52
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java60
-rw-r--r--services/core/java/com/android/server/pm/SettingsXml.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutBitmapSaver.java15
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java181
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java188
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java11
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/OWNERS6
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java31
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java28
-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/DomainVerificationLegacySettings.java1
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java40
-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.java125
-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/DomainVerificationUtils.java61
-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/pm/verify/domain/proxy/DomainVerificationProxyV1.java12
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java10
-rw-r--r--services/core/java/com/android/server/policy/ModifierShortcutManager.java (renamed from services/core/java/com/android/server/policy/ShortcutManager.java)202
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java190
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java20
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/core/java/com/android/server/role/OWNERS6
-rw-r--r--services/core/java/com/android/server/servicewatcher/OWNERS5
-rw-r--r--services/core/java/com/android/server/servicewatcher/ServiceWatcher.java (renamed from services/core/java/com/android/server/ServiceWatcher.java)3
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java41
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java3
-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.java60
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java4
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerInternal.java53
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java34
-rw-r--r--services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java6
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java44
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java103
-rw-r--r--services/core/java/com/android/server/vcn/VcnNetworkProvider.java12
-rw-r--r--services/core/java/com/android/server/vibrator/InputDeviceDelegate.java23
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java4
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java84
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java18
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java25
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java175
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java10
-rw-r--r--services/core/java/com/android/server/wm/BlurController.java76
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java25
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java289
-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/InsetsStateController.java3
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java10
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java28
-rw-r--r--services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java6
-rw-r--r--services/core/java/com/android/server/wm/Task.java64
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java239
-rw-r--r--services/core/java/com/android/server/wm/Transition.java46
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java14
-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.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java96
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java36
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java154
-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/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp69
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp11
-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/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java8
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt40
-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.kt35
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt4
-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/display/LocalDisplayAdapterTest.java1
-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/BootReceiverTest.java72
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java581
-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/KeyEventDispatcherTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java)80
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java327
-rw-r--r--services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java77
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java8
-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.java113
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java92
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java85
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java77
-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.java11
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerService.java126
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java37
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceShellCommand.java79
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java6
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.aidl22
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.java22
-rwxr-xr-xtelecomm/java/android/telecom/Call.java58
-rw-r--r--telecomm/java/android/telecom/CallDiagnosticService.java328
-rw-r--r--telecomm/java/android/telecom/Connection.java40
-rw-r--r--telecomm/java/android/telecom/DiagnosticCall.java381
-rw-r--r--telecomm/java/android/telecom/Log.java2
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl37
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl32
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl4
-rw-r--r--telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java16
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java1
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java8
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java93
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java13
-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/SipMessage.java29
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsConfig.aidl2
-rw-r--r--telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java4
-rw-r--r--telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java4
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java10
-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--telephony/java/com/android/internal/telephony/ITelephony.aidl9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt2
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml18
-rw-r--r--tests/HwAccelerationTest/res/layout/stretch_layout.xml88
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java36
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java540
-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/UpdatableSystemFontTest/Android.bp4
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml1
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java25
-rw-r--r--tests/net/common/java/android/net/CaptivePortalTest.java20
-rw-r--r--tests/net/common/java/android/net/NetworkStateSnapshotTest.kt6
-rw-r--r--tests/net/common/java/android/net/UidRangeTest.java113
-rw-r--r--tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt10
-rw-r--r--tests/net/java/android/net/IpSecAlgorithmTest.java1
-rw-r--r--tests/net/java/android/net/NetworkTemplateTest.kt29
-rw-r--r--tests/net/java/android/net/UidRangeTest.java67
-rw-r--r--tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt6
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java835
-rw-r--r--tests/net/java/com/android/server/connectivity/DnsManagerTest.java35
-rw-r--r--tests/net/java/com/android/server/connectivity/LingerMonitorTest.java6
-rw-r--r--tests/net/java/com/android/server/connectivity/Nat464XlatTest.java20
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java101
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java92
-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/IkeTrafficSelectorUtilsTest.java66
-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--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java26
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java20
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java20
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java12
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java21
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java73
-rw-r--r--tools/aapt2/cmd/Link.cpp10
-rw-r--r--tools/aapt2/cmd/Link_test.cpp77
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp1
-rw-r--r--tools/aapt2/java/ClassDefinition.h29
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp63
-rw-r--r--tools/aapt2/java/JavaClassGenerator.h2
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp2
-rw-r--r--tools/aapt2/process/SymbolTable.cpp4
-rw-r--r--tools/aapt2/test/Fixture.cpp85
-rw-r--r--tools/aapt2/test/Fixture.h35
-rw-r--r--tools/bit/make.cpp15
-rwxr-xr-xtools/fonts/fontchain_linter.py8
874 files changed, 30337 insertions, 10695 deletions
diff --git a/Android.bp b/Android.bp
index 908280e5d7f3..72fc1030b63b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -344,8 +344,8 @@ filegroup {
genrule {
name: "statslog-telephony-common-java-gen",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
- + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
+ cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" +
+ " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
}
@@ -421,6 +421,7 @@ filegroup {
":resourcemanager_aidl",
":storaged_aidl",
":vold_aidl",
+ ":deviceproductinfoconstants_aidl",
// For the generated R.java and Manifest.java
":framework-res{.aapt.srcjar}",
@@ -580,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",
@@ -749,8 +752,8 @@ java_library {
}
platform_compat_config {
- name: "framework-platform-compat-config",
- src: ":framework-minus-apex",
+ name: "framework-platform-compat-config",
+ src: ":framework-minus-apex",
}
// A temporary build target that is conditionally included on the bootclasspath if
@@ -771,7 +774,7 @@ genrule {
name: "statslog-framework-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module framework" +
- " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
+ " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
out: ["com/android/internal/util/FrameworkStatsLog.java"],
}
@@ -880,7 +883,7 @@ filegroup {
java_library {
name: "framework-annotations-lib",
- srcs: [ ":framework-annotations" ],
+ srcs: [":framework-annotations"],
sdk_version: "core_current",
}
@@ -1158,7 +1161,6 @@ cc_library {
},
}
-
// This is the full proto version of libplatformprotos. It may only
// be used by test code that is not shipped on the device.
cc_library {
@@ -1224,68 +1226,57 @@ filegroup {
path: "core/java",
}
-aidl_interface {
- name: "libincremental_aidl",
- unstable: true,
+cc_defaults {
+ name: "incremental_default",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wextra-semi",
+ "-Werror",
+ "-Wzero-as-null-pointer-constant",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ aidl: {
+ include_dirs: [
+ "frameworks/native/aidl/binder",
+ ],
+ export_aidl_headers: true,
+ },
+}
+
+cc_library {
+ name: "libincremental_aidl-cpp",
srcs: [
":incremental_aidl",
],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: true,
- },
- },
+ defaults: ["incremental_default"],
}
-aidl_interface {
- name: "libdataloader_aidl",
- unstable: true,
+cc_library {
+ name: "libdataloader_aidl-cpp",
srcs: [
":dataloader_aidl",
],
- imports: [
- "libincremental_aidl",
+ defaults: ["incremental_default"],
+ shared_libs: [
+ "libincremental_aidl-cpp",
],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: false,
- },
- },
}
-aidl_interface {
- name: "libincremental_manager_aidl",
- unstable: true,
+cc_library {
+ name: "libincremental_manager_aidl-cpp",
srcs: [
":incremental_manager_aidl",
],
- imports: [
- "libincremental_aidl",
- "libdataloader_aidl",
+ defaults: ["incremental_default"],
+ shared_libs: [
+ "libincremental_aidl-cpp",
+ "libdataloader_aidl-cpp",
],
- backend: {
- java: {
- sdk_version: "28",
- },
- cpp: {
- enabled: true,
- },
- ndk: {
- enabled: false,
- },
- },
}
// TODO(b/77285514): remove this once the last few hidl interfaces have been
@@ -1314,7 +1305,7 @@ java_library {
"core/java/android/os/RemoteException.java",
"core/java/android/util/AndroidException.java",
],
- libs: [ "unsupportedappusage" ],
+ libs: ["unsupportedappusage"],
dxflags: ["--core-library"],
installable: false,
@@ -1533,4 +1524,5 @@ java_library {
":protolog-common-src",
],
}
+
// protolog end
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 30ed7de92614..175fb38bafc7 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[Builtin Hooks]
clang_format = true
+bpfmt = true
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
@@ -15,7 +16,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
services/incremental/
tests/
tools/
-
+bpfmt = -d
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
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/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
index 1d94d7e8eca2..d5ed95f18f93 100644
--- a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt
@@ -97,11 +97,21 @@ class PackageParsingPerfTest {
private val state: BenchmarkState get() = perfStatusReporter.benchmarkState
private val apks: List<File> get() = params.apks
+ private fun safeParse(parser: ParallelParser<*>, file: File) {
+ try {
+ parser.parse(file)
+ } catch (e: Exception) {
+ // ignore
+ }
+ }
+
@Test
fun sequentialNoCache() {
params.cacheDirToParser(null).use { parser ->
while (state.keepRunning()) {
- apks.forEach { parser.parse(it) }
+ apks.forEach {
+ safeParse(parser, it)
+ }
}
}
}
@@ -110,10 +120,10 @@ class PackageParsingPerfTest {
fun sequentialCached() {
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
// Fill the cache
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
while (state.keepRunning()) {
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
}
}
}
@@ -132,7 +142,7 @@ class PackageParsingPerfTest {
fun parallelCached() {
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
// Fill the cache
- apks.forEach { parser.parse(it) }
+ apks.forEach { safeParse(parser, it) }
while (state.keepRunning()) {
apks.forEach { parser.submit(it) }
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 168c7c2f13cd..5ded446ff81a 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -207,7 +207,6 @@ package android.app.appsearch {
public static final class GetByUriRequest.Builder {
ctor public GetByUriRequest.Builder();
- method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>);
@@ -323,7 +322,6 @@ package android.app.appsearch {
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
method @NonNull public android.app.appsearch.SearchSpec build();
method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int);
@@ -338,7 +336,8 @@ package android.app.appsearch {
public final class SetSchemaRequest {
method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.AppSearchSchema.Migrator> getMigrators();
method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
- method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
+ method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
+ method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages();
method public boolean isForceOverride();
}
@@ -350,8 +349,9 @@ package android.app.appsearch {
method @NonNull public android.app.appsearch.SetSchemaRequest build();
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
+ method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
}
public class SetSchemaResponse {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 6a5975ef7ff7..da446bf1e7c6 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -317,7 +317,7 @@ public class AppSearchManager {
getPackageName(),
DEFAULT_DATABASE_NAME,
schemaBundles,
- new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+ new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
request.isForceOverride(),
mContext.getUserId(),
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 24cc60e5eef5..f379739a3778 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -146,7 +146,7 @@ public final class AppSearchSession implements Closeable {
mPackageName,
mDatabaseName,
schemaBundles,
- new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
+ new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles,
request.isForceOverride(),
mUserId,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 8dd9dc1be312..fb63e16aa6a8 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -96,7 +96,7 @@ public class GlobalSearchSession implements Closeable {
* SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
*
* <p>Document access can also be granted to system UIs by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema.
+ * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema.
*
* <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
*
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 68ae23f9a9db..ba27762c7dfb 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -33,7 +33,7 @@ interface IAppSearchManager {
* @param packageName The name of the package that owns this schema.
* @param databaseName The name of the database where this schema lives.
* @param schemaBundles List of {@link AppSearchSchema} bundles.
- * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+ * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
* surfaces.
* @param schemasPackageAccessibleBundles Schema types that are visible to the specified
* packages. The value List contains PackageIdentifier Bundles.
@@ -47,7 +47,7 @@ interface IAppSearchManager {
in String packageName,
in String databaseName,
in List<Bundle> schemaBundles,
- in List<String> schemasNotPlatformSurfaceable,
+ in List<String> schemasNotDisplayedBySystem,
in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
in int userId,
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index c927e3412cbc..17266f82b603 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -166,29 +166,7 @@ public final class GetByUriRequest {
* all results, excepting any types that have their own, specific property paths set.
*
* @throws IllegalStateException if the builder has already been used.
- * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
- */
- @NonNull
- public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) {
- Preconditions.checkNotNull(propertyPaths);
- return addProjection(schemaType, Arrays.asList(propertyPaths));
- }
-
- /**
- * Adds property paths for the specified type to be used for projection. If property paths
- * are added for a type, then only the properties referred to will be retrieved for results
- * of that type. If a property path that is specified isn't present in a result, it will be
- * ignored for that result. Property paths cannot be null.
- *
- * <p>If no property paths are added for a particular type, then all properties of results
- * of that type will be retrieved.
- *
- * <p>If property path is added for the {@link
- * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
- * all results, excepting any types that have their own, specific property paths set.
- *
- * @throws IllegalStateException if the builder has already been used.
- * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+ * @see SearchSpec.Builder#addProjection
*/
@NonNull
public Builder addProjection(
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index d792f450bc05..f34034b1c5c0 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -202,14 +202,6 @@ public final class SearchResult {
*/
public static final String PROPERTY_PATH_FIELD = "propertyPath";
- /**
- * The index of matching value in its property. A property may have multiple values. This
- * index indicates which value is the match.
- *
- * @hide
- */
- public static final String VALUES_INDEX_FIELD = "valuesIndex";
-
/** @hide */
public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
@@ -232,8 +224,7 @@ public final class SearchResult {
mBundle = Preconditions.checkNotNull(bundle);
Preconditions.checkNotNull(document);
mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
- mFullText =
- getPropertyValues(document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
+ mFullText = getPropertyValues(document, mPropertyPath);
}
/**
@@ -326,8 +317,7 @@ public final class SearchResult {
}
/** Extracts the matching string from the document. */
- private static String getPropertyValues(
- GenericDocument document, String propertyName, int valueIndex) {
+ private static String getPropertyValues(GenericDocument document, String propertyName) {
// In IcingLib snippeting is available for only 3 data types i.e String, double and
// long, so we need to check which of these three are requested.
// TODO (tytytyww): getPropertyStringArray takes property name, handle for property
@@ -337,7 +327,9 @@ public final class SearchResult {
if (values == null) {
throw new IllegalStateException("No content found for requested property path!");
}
- return values[valueIndex];
+
+ // TODO(b/175146044): Return the proper match based on the index in the propertyName.
+ return values[0];
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index f72f8e1bbc5d..7888c8d78cd8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -535,28 +535,6 @@ public final class SearchSpec {
*/
@NonNull
public SearchSpec.Builder addProjection(
- @NonNull String schema, @NonNull String... propertyPaths) {
- Preconditions.checkNotNull(propertyPaths);
- return addProjection(schema, Arrays.asList(propertyPaths));
- }
-
- /**
- * Adds property paths for the specified type to be used for projection. If property paths
- * are added for a type, then only the properties referred to will be retrieved for results
- * of that type. If a property path that is specified isn't present in a result, it will be
- * ignored for that result. Property paths cannot be null.
- *
- * <p>If no property paths are added for a particular type, then all properties of results
- * of that type will be retrieved.
- *
- * <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
- * then those property paths will apply to all results, excepting any types that have their
- * own, specific property paths set.
- *
- * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
- */
- @NonNull
- public SearchSpec.Builder addProjection(
@NonNull String schema, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schema);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 426a903981b3..c05406358995 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -71,19 +71,19 @@ import java.util.Set;
*/
public final class SetSchemaRequest {
private final Set<AppSearchSchema> mSchemas;
- private final Set<String> mSchemasNotVisibleToSystemUi;
+ private final Set<String> mSchemasNotDisplayedBySystem;
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
private final Map<String, AppSearchSchema.Migrator> mMigrators;
private final boolean mForceOverride;
SetSchemaRequest(
@NonNull Set<AppSearchSchema> schemas,
- @NonNull Set<String> schemasNotVisibleToSystemUi,
+ @NonNull Set<String> schemasNotDisplayedBySystem,
@NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
@NonNull Map<String, AppSearchSchema.Migrator> migrators,
boolean forceOverride) {
mSchemas = Preconditions.checkNotNull(schemas);
- mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi);
+ mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem);
mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
mMigrators = Preconditions.checkNotNull(migrators);
mForceOverride = forceOverride;
@@ -96,12 +96,22 @@ public final class SetSchemaRequest {
}
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ * @deprecated This method exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ @NonNull
+ public Set<String> getSchemasNotVisibleToSystemUi() {
+ return getSchemasNotDisplayedBySystem();
+ }
+
+ /**
* Returns all the schema types that are opted out of being displayed and visible on any system
* UI surface.
*/
@NonNull
- public Set<String> getSchemasNotVisibleToSystemUi() {
- return Collections.unmodifiableSet(mSchemasNotVisibleToSystemUi);
+ public Set<String> getSchemasNotDisplayedBySystem() {
+ return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem);
}
/**
@@ -151,7 +161,7 @@ public final class SetSchemaRequest {
*/
public static final class Builder {
private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
- private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
+ private final Set<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
new ArrayMap<>();
private final Map<String, AppSearchSchema.Migrator> mMigrators = new ArrayMap<>();
@@ -163,6 +173,8 @@ public final class SetSchemaRequest {
*
* <p>An {@link AppSearchSchema} object represents one type of structured data.
*
+ * <p>Any documents of these types will be displayed on system UI surfaces by default.
+ *
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
@@ -187,30 +199,43 @@ public final class SetSchemaRequest {
}
/**
+ * TODO(b/181887768): This method exists only for dogfooder transition and must be removed.
+ * @deprecated This method exists only for dogfooder transition and must be removed.
+ */
+ @Deprecated
+ @NonNull
+ public Builder setSchemaTypeVisibilityForSystemUi(
+ @NonNull String schemaType, boolean displayed) {
+ return setSchemaTypeDisplayedBySystem(schemaType, displayed);
+ }
+
+ /**
* Sets whether or not documents from the provided {@code schemaType} will be displayed and
* visible on any system UI surface.
*
* <p>This setting applies to the provided {@code schemaType} only, and does not persist
* across {@link AppSearchSession#setSchema} calls.
*
- * <p>By default, documents are displayed and visible on system UI surfaces.
+ * <p>The default behavior, if this method is not called, is to allow types to be displayed
+ * on system UI surfaces.
*
- * @param schemaType The schema type to set visibility on.
- * @param visible Whether the {@code schemaType} will be visible or not.
+ * @param schemaType The name of an {@link AppSearchSchema} within the same {@link
+ * SetSchemaRequest}, which will be configured.
+ * @param displayed Whether documents of this type will be displayed on system UI surfaces.
* @throws IllegalStateException if the builder has already been used.
*/
- // Merged list available from getSchemasNotVisibleToSystemUi
+ // Merged list available from getSchemasNotDisplayedBySystem
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
- public Builder setSchemaTypeVisibilityForSystemUi(
- @NonNull String schemaType, boolean visible) {
+ public Builder setSchemaTypeDisplayedBySystem(
+ @NonNull String schemaType, boolean displayed) {
Preconditions.checkNotNull(schemaType);
Preconditions.checkState(!mBuilt, "Builder has already been used");
- if (visible) {
- mSchemasNotVisibleToSystemUi.remove(schemaType);
+ if (displayed) {
+ mSchemasNotDisplayedBySystem.remove(schemaType);
} else {
- mSchemasNotVisibleToSystemUi.add(schemaType);
+ mSchemasNotDisplayedBySystem.add(schemaType);
}
return this;
}
@@ -315,9 +340,10 @@ public final class SetSchemaRequest {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mBuilt = true;
- // Verify that any schema types with visibility settings refer to a real schema.
+ // Verify that any schema types with display or visibility settings refer to a real
+ // schema.
// Create a copy because we're going to remove from the set for verification purposes.
- Set<String> referencedSchemas = new ArraySet<>(mSchemasNotVisibleToSystemUi);
+ Set<String> referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem);
referencedSchemas.addAll(mSchemasVisibleToPackages.keySet());
for (AppSearchSchema schema : mSchemas) {
@@ -332,7 +358,7 @@ public final class SetSchemaRequest {
return new SetSchemaRequest(
mSchemas,
- mSchemasNotVisibleToSystemUi,
+ mSchemasNotDisplayedBySystem,
mSchemasVisibleToPackages,
mMigrators,
mForceOverride);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index a45fa39bd58d..27c9ccb2cca6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -89,7 +89,7 @@ public class AppSearchManagerService extends SystemService {
@NonNull String packageName,
@NonNull String databaseName,
@NonNull List<Bundle> schemaBundles,
- @NonNull List<String> schemasNotPlatformSurfaceable,
+ @NonNull List<String> schemasNotDisplayedBySystem,
@NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
@UserIdInt int userId,
@@ -119,13 +119,12 @@ public class AppSearchManagerService extends SystemService {
}
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
}
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.setSchema(
packageName,
databaseName,
schemas,
- schemasNotPlatformSurfaceable,
+ schemasNotDisplayedBySystem,
schemasPackageAccessible,
forceOverride);
invokeCallbackOnResult(
@@ -153,7 +152,7 @@ public class AppSearchManagerService extends SystemService {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
for (int i = 0; i < schemas.size(); i++) {
@@ -188,7 +187,7 @@ public class AppSearchManagerService extends SystemService {
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
for (int i = 0; i < documentBundles.size(); i++) {
GenericDocument document = new GenericDocument(documentBundles.get(i));
try {
@@ -231,7 +230,7 @@ public class AppSearchManagerService extends SystemService {
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
for (int i = 0; i < uris.size(); i++) {
String uri = uris.get(i);
try {
@@ -276,7 +275,7 @@ public class AppSearchManagerService extends SystemService {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
SearchResultPage searchResultPage =
impl.query(
packageName,
@@ -311,7 +310,7 @@ public class AppSearchManagerService extends SystemService {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
SearchResultPage searchResultPage =
impl.globalQuery(
queryExpression,
@@ -342,7 +341,7 @@ public class AppSearchManagerService extends SystemService {
try {
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
invokeCallbackOnResult(
callback,
@@ -362,7 +361,7 @@ public class AppSearchManagerService extends SystemService {
try {
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.invalidateNextPageToken(nextPageToken);
} catch (Throwable t) {
Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -390,7 +389,7 @@ public class AppSearchManagerService extends SystemService {
try {
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
invokeCallbackOnResult(
callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
@@ -422,7 +421,7 @@ public class AppSearchManagerService extends SystemService {
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
for (int i = 0; i < uris.size(); i++) {
String uri = uris.get(i);
try {
@@ -460,7 +459,7 @@ public class AppSearchManagerService extends SystemService {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.removeByQuery(
packageName,
databaseName,
@@ -482,7 +481,7 @@ public class AppSearchManagerService extends SystemService {
try {
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.persistToDisk();
} catch (Throwable t) {
Log.e(TAG, "Unable to persist the data to disk", t);
@@ -499,7 +498,7 @@ public class AppSearchManagerService extends SystemService {
final long callingIdentity = Binder.clearCallingIdentity();
try {
verifyUserUnlocked(callingUserId);
- mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
@@ -571,6 +570,7 @@ public class AppSearchManagerService extends SystemService {
private void invokeCallbackOnError(
IAppSearchBatchResultCallback callback, Throwable throwable) {
try {
+ //TODO(b/175067650) verify ParcelableException could propagate throwable correctly.
callback.onSystemError(new ParcelableException(throwable));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send error to the callback", e);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 82319d4f353b..8c953d12fd98 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -74,7 +74,7 @@ public final class ImplInstanceManager {
}
/**
- * Gets an instance of AppSearchImpl for the given user.
+ * Gets an instance of AppSearchImpl for the given user, or creates one if none exists.
*
* <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
* one will be created.
@@ -84,7 +84,7 @@ public final class ImplInstanceManager {
* @return An initialized {@link AppSearchImpl} for this user
*/
@NonNull
- public AppSearchImpl getAppSearchImpl(
+ public AppSearchImpl getOrCreateAppSearchImpl(
@NonNull Context context, @UserIdInt int userId) throws AppSearchException {
synchronized (mInstancesLocked) {
AppSearchImpl instance = mInstancesLocked.get(userId);
@@ -96,6 +96,32 @@ public final class ImplInstanceManager {
}
}
+
+ /**
+ * Gets an instance of AppSearchImpl for the given user.
+ *
+ * <p>This method should only be called by an initialized SearchSession, which has been already
+ * created the AppSearchImpl instance for the given user.
+ *
+ * @param userId The multi-user userId of the device user calling AppSearch
+ * @return An initialized {@link AppSearchImpl} for this user
+ * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user.
+ */
+ @NonNull
+ public AppSearchImpl getAppSearchImpl(@UserIdInt int userId) {
+ synchronized (mInstancesLocked) {
+ AppSearchImpl instance = mInstancesLocked.get(userId);
+ if (instance == null) {
+ // Impossible scenario, user cannot call an uninitialized SearchSession,
+ // getInstance should always find the instance for the given user and never try to
+ // create an instance for this user again.
+ throw new IllegalStateException(
+ "AppSearchImpl has never been created for this user: " + userId);
+ }
+ return instance;
+ }
+ }
+
private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
File appSearchDir = getAppSearchDir(context, userId);
@@ -104,6 +130,7 @@ public final class ImplInstanceManager {
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
// See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
+ //TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory
File userCeDir =
Environment.getDataUserCePackageDirectory(
StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index f422ebc2db7b..e9852aa1cd41 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -113,8 +113,6 @@ public class SearchResultToProtoConverter {
Bundle bundle = new Bundle();
bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath);
bundle.putInt(
- SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex());
- bundle.putInt(
SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD,
snippetMatchProto.getExactMatchPosition());
bundle.putInt(
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 2c9477a5d76b..41c70f072262 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I593dfd22279739e5f578e07d36a55cf02ee942c5
+I42b89416968565ceb6483b400894f5b49524208c
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index eb072afec696..3c5082ef937d 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -30,5 +30,8 @@ java_library {
"guava",
"truth-prebuilt",
],
- visibility: ["//cts/tests/appsearch"],
+ visibility: [
+ "//cts/tests/appsearch",
+ "//vendor:__subpackages__",
+ ],
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
index d912c08e7d5f..440050faaa7d 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java
@@ -34,8 +34,8 @@ public interface GlobalSearchSessionShim extends Closeable {
* SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema.
*
* <p>Document access can also be granted to system UIs by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link
- * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema.
+ * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or {@link
+ * SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem} when building a schema.
*
* <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query
* string.
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index df690d00a322..b1b733a599c6 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -108,6 +108,8 @@ public class PowerWhitelistManager {
* @hide
*/
public static final int REASON_DENIED = -1;
+
+ /* Reason code range 0-9 are reserved for default reasons */
/**
* The default reason code if reason is unknown.
*/
@@ -116,6 +118,8 @@ public class PowerWhitelistManager {
* Use REASON_OTHER if there is no better choice.
*/
public static final int REASON_OTHER = 1;
+
+ /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
/** @hide */
public static final int REASON_PROC_STATE_PERSISTENT = 10;
/** @hide */
@@ -128,6 +132,8 @@ public class PowerWhitelistManager {
public static final int REASON_PROC_STATE_FGS = 14;
/** @hide */
public static final int REASON_PROC_STATE_BFGS = 15;
+
+ /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
/** @hide */
public static final int REASON_UID_VISIBLE = 50;
/** @hide */
@@ -166,114 +172,126 @@ public class PowerWhitelistManager {
public static final int REASON_EXEMPTED_PACKAGE = 64;
/** @hide */
public static final int REASON_ALLOWLISTED_PACKAGE = 65;
- /**
- * If it's because of a role,
- * @hide
- */
+ /** @hide */
public static final int REASON_APPOP = 66;
/* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
- Reason code for temp and system allowlist starts here.
- */
+ Reason code for temp and system allowlist starts here.
+ Reason code range 100-199 are reserved for public reasons. */
+ /**
+ * Set temp-allowlist for location geofence purpose.
+ */
public static final int REASON_GEOFENCING = 100;
+ /**
+ * Set temp-allowlist for server push messaging.
+ */
public static final int REASON_PUSH_MESSAGING = 101;
- public static final int REASON_ACTIVITY_RECOGNITION = 102;
+ /**
+ * Set temp-allowlist for server push messaging over the quota.
+ */
+ public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+ /**
+ * Set temp-allowlist for activity recognition.
+ */
+ public static final int REASON_ACTIVITY_RECOGNITION = 103;
+ /* Reason code range 200-299 are reserved for broadcast actions */
/**
* Broadcast ACTION_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_BOOT_COMPLETED = 103;
+ public static final int REASON_BOOT_COMPLETED = 200;
/**
* Broadcast ACTION_PRE_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_PRE_BOOT_COMPLETED = 104;
-
+ public static final int REASON_PRE_BOOT_COMPLETED = 201;
/**
* Broadcast ACTION_LOCKED_BOOT_COMPLETED.
* @hide
*/
- public static final int REASON_LOCKED_BOOT_COMPLETED = 105;
+ public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+
+ /* Reason code range 300-399 are reserved for other internal reasons */
/**
* Device idle system allowlist, including EXCEPT-IDLE
* @hide
*/
- public static final int REASON_SYSTEM_ALLOW_LISTED = 106;
+ public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
/** @hide */
- public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 107;
+ public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
/**
* AlarmManagerService.
* @hide
*/
- public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 108;
+ public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
/**
* ActiveServices.
* @hide
*/
- public static final int REASON_SERVICE_LAUNCH = 109;
+ public static final int REASON_SERVICE_LAUNCH = 303;
/**
* KeyChainSystemService.
* @hide
*/
- public static final int REASON_KEY_CHAIN = 110;
+ public static final int REASON_KEY_CHAIN = 304;
/**
* PackageManagerService.
* @hide
*/
- public static final int REASON_PACKAGE_VERIFIER = 111;
+ public static final int REASON_PACKAGE_VERIFIER = 305;
/**
* SyncManager.
* @hide
*/
- public static final int REASON_SYNC_MANAGER = 112;
+ public static final int REASON_SYNC_MANAGER = 306;
/**
* DomainVerificationProxyV1.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V1 = 113;
+ public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
/**
* DomainVerificationProxyV2.
* @hide
*/
- public static final int REASON_DOMAIN_VERIFICATION_V2 = 114;
+ public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
/** @hide */
- public static final int REASON_VPN = 115;
+ public static final int REASON_VPN = 309;
/**
* NotificationManagerService.
* @hide
*/
- public static final int REASON_NOTIFICATION_SERVICE = 116;
+ public static final int REASON_NOTIFICATION_SERVICE = 310;
/**
* Broadcast ACTION_MY_PACKAGE_REPLACED.
* @hide
*/
- public static final int REASON_PACKAGE_REPLACED = 117;
+ public static final int REASON_PACKAGE_REPLACED = 311;
/**
* LocationProviderManager.
* @hide
*/
- public static final int REASON_LOCATION_PROVIDER = 118;
+ public static final int REASON_LOCATION_PROVIDER = 312;
/**
* MediaButtonReceiver.
* @hide
*/
- public static final int REASON_MEDIA_BUTTON = 119;
+ public static final int REASON_MEDIA_BUTTON = 313;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_SMS = 120;
+ public static final int REASON_EVENT_SMS = 314;
/**
* InboundSmsHandler.
* @hide
*/
- public static final int REASON_EVENT_MMS = 121;
+ public static final int REASON_EVENT_MMS = 315;
/**
* Shell app.
* @hide
*/
- public static final int REASON_SHELL = 122;
+ public static final int REASON_SHELL = 316;
/**
* The list of BG-FGS-Launch and temp-allowlist reason code.
@@ -310,6 +328,7 @@ public class PowerWhitelistManager {
// temp and system allowlist reasons.
REASON_GEOFENCING,
REASON_PUSH_MESSAGING,
+ REASON_PUSH_MESSAGING_OVER_QUOTA,
REASON_ACTIVITY_RECOGNITION,
REASON_BOOT_COMPLETED,
REASON_PRE_BOOT_COMPLETED,
@@ -589,6 +608,8 @@ public class PowerWhitelistManager {
return "GEOFENCING";
case REASON_PUSH_MESSAGING:
return "PUSH_MESSAGING";
+ case REASON_PUSH_MESSAGING_OVER_QUOTA:
+ return "PUSH_MESSAGING_OVER_QUOTA";
case REASON_ACTIVITY_RECOGNITION:
return "ACTIVITY_RECOGNITION";
case REASON_BOOT_COMPLETED:
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 5e5717d11432..0dde546e85de 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -32,12 +32,8 @@ public interface DeviceIdleInternal {
void exitIdle(String reason);
- // duration in milliseconds
- void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
- long duration, int userId, boolean sync, String reason);
-
void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
- long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+ long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
@Nullable String reason);
/**
@@ -49,10 +45,11 @@ public interface DeviceIdleInternal {
* @param sync
* @param reasonCode one of {@link ReasonCode}
* @param reason
+ * @param callingUid UID of app who added this temp-allowlist.
*/
void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
@TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
- @Nullable String reason);
+ @Nullable String reason, int callingUid);
// duration in milliseconds
long getNotificationAllowlistDuration();
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index ac28e828eb2e..84fb39b273c4 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1943,19 +1943,11 @@ public class DeviceIdleController extends SystemService
}
// duration in milliseconds
- @Deprecated
@Override
public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
- long duration, int userId, boolean sync, @Nullable String reason) {
- addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
- userId, sync, REASON_UNKNOWN, reason);
- }
-
- @Override
- public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
- long duration, int userId, boolean sync, @ReasonCode int reasonCode,
+ long durationMs, int userId, boolean sync, @ReasonCode int reasonCode,
@Nullable String reason) {
- addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration,
+ addPowerSaveTempAllowlistAppInternal(callingUid, packageName, durationMs,
userId, sync, reasonCode, reason);
}
@@ -1963,8 +1955,8 @@ public class DeviceIdleController extends SystemService
@Override
public void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
@TempAllowListType int type, boolean sync, @ReasonCode int reasonCode,
- @Nullable String reason) {
- addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync,
+ @Nullable String reason, int callingUid) {
+ addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, type, sync,
reasonCode, reason);
}
@@ -2741,6 +2733,16 @@ public class DeviceIdleController extends SystemService
void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName,
long duration, int userId, boolean sync, @ReasonCode int reasonCode,
@Nullable String reason) {
+ synchronized (this) {
+ int callingAppId = UserHandle.getAppId(callingUid);
+ if (callingAppId >= Process.FIRST_APPLICATION_UID) {
+ if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) {
+ throw new SecurityException(
+ "Calling app " + UserHandle.formatUid(callingUid)
+ + " is not on whitelist");
+ }
+ }
+ }
try {
int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration,
@@ -2760,13 +2762,6 @@ public class DeviceIdleController extends SystemService
boolean informWhitelistChanged = false;
int appId = UserHandle.getAppId(uid);
synchronized (this) {
- int callingAppId = UserHandle.getAppId(callingUid);
- if (callingAppId >= Process.FIRST_APPLICATION_UID) {
- if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) {
- throw new SecurityException("Calling app " + UserHandle.formatUid(callingUid)
- + " is not on whitelist");
- }
- }
duration = Math.min(duration, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId);
final boolean newEntry = entry == null;
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..b958c3f694bf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -25,6 +25,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.UserSwitchObserver;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -73,40 +74,79 @@ 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 is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher
+ * state, or is allowed to run as an expedited job, but is for a completely background user.
+ */
+ static final int WORK_TYPE_BGUSER_IMPORTANT = 1 << 4;
+ /**
+ * 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 << 5;
@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 = 6;
+ 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_IMPORTANT,
WORK_TYPE_BGUSER
})
@Retention(RetentionPolicy.SOURCE)
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:
return "BG";
case WORK_TYPE_BGUSER:
return "BGUSER";
+ case WORK_TYPE_BGUSER_IMPORTANT:
+ return "BGUSER_IMPORTANT";
default:
return "WORK(" + workType + ")";
}
@@ -131,60 +171,82 @@ 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),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ List.of(Pair.create(WORK_TYPE_BG, 6),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
+ Pair.create(WORK_TYPE_BGUSER, 3))
),
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, 1),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ List.of(Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+ Pair.create(WORK_TYPE_BGUSER, 1))
),
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))
+ List.of(Pair.create(WORK_TYPE_BG, 1),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 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))
+ List.of(Pair.create(WORK_TYPE_BG, 1),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 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),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ List.of(Pair.create(WORK_TYPE_BG, 6),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 2),
+ Pair.create(WORK_TYPE_BGUSER, 3))
),
- 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),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ List.of(Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+ Pair.create(WORK_TYPE_BGUSER, 1))
),
- 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))
+ List.of(Pair.create(WORK_TYPE_BG, 1),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 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))
+ List.of(Pair.create(WORK_TYPE_BG, 1),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+ Pair.create(WORK_TYPE_BGUSER, 1))
)
);
@@ -255,6 +317,8 @@ class JobConcurrencyManager {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
try {
ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
@@ -278,6 +342,20 @@ class JobConcurrencyManager {
case Intent.ACTION_SCREEN_OFF:
onInteractiveStateChanged(false);
break;
+ case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ if (mPowerManager != null && mPowerManager.isDeviceIdleMode()) {
+ synchronized (mLock) {
+ stopLongRunningJobsLocked("deep doze");
+ }
+ }
+ break;
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+ if (mPowerManager != null && mPowerManager.isPowerSaveMode()) {
+ synchronized (mLock) {
+ stopLongRunningJobsLocked("battery saver");
+ }
+ }
+ break;
}
}
};
@@ -436,7 +514,7 @@ class JobConcurrencyManager {
// Update the priorities of jobs that aren't running, and also count the pending work types.
// Do this before the following loop to hopefully reduce the cost of
// shouldStopRunningJobLocked().
- updateNonRunningPriorities(pendingJobs, true);
+ updateNonRunningPrioritiesLocked(pendingJobs, true);
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = activeServices.get(i);
@@ -552,7 +630,8 @@ class JobConcurrencyManager {
+ activeServices.get(i).getRunningJobLocked());
}
// preferredUid will be set to uid of currently running job.
- activeServices.get(i).preemptExecutingJobLocked(preemptReasonForContext[i]);
+ activeServices.get(i).cancelExecutingJobLocked(
+ JobParameters.REASON_PREEMPT, preemptReasonForContext[i]);
preservePreferredUid = true;
} else {
final JobStatus pendingJob = contextIdToJobMap[i];
@@ -571,13 +650,26 @@ class JobConcurrencyManager {
noteConcurrency();
}
+ @GuardedBy("mLock")
+ private void stopLongRunningJobsLocked(@NonNull String debugReason) {
+ for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
+ final JobServiceContext jsc = mService.mActiveServices.get(i);
+ final JobStatus jobStatus = jsc.getRunningJobLocked();
+
+ if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) {
+ jsc.cancelExecutingJobLocked(JobParameters.REASON_TIMEOUT, debugReason);
+ }
+ }
+ }
+
private void noteConcurrency() {
mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
// TODO: log per type instead of only TOP
mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP));
}
- private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs,
+ @GuardedBy("mLock")
+ private void updateNonRunningPrioritiesLocked(@NonNull final List<JobStatus> pendingJobs,
boolean updateCounter) {
for (int i = 0; i < pendingJobs.size(); i++) {
final JobStatus pending = pendingJobs.get(i);
@@ -595,6 +687,7 @@ class JobConcurrencyManager {
}
}
+ @GuardedBy("mLock")
private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
@@ -614,6 +707,7 @@ class JobConcurrencyManager {
}
}
+ @GuardedBy("mLock")
void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
@WorkType final int workType) {
mWorkCountTracker.onJobFinished(workType);
@@ -622,7 +716,7 @@ class JobConcurrencyManager {
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
updateCounterConfigLocked();
// Preemption case needs special care.
- updateNonRunningPriorities(pendingJobs, false);
+ updateNonRunningPrioritiesLocked(pendingJobs, false);
JobStatus highestPriorityJob = null;
int highPriWorkType = workType;
@@ -693,7 +787,7 @@ class JobConcurrencyManager {
}
} else if (pendingJobs.size() > 0) {
updateCounterConfigLocked();
- updateNonRunningPriorities(pendingJobs, false);
+ updateNonRunningPrioritiesLocked(pendingJobs, false);
// This slot is now free and we have pending jobs. Start the highest priority job we
// find.
@@ -742,6 +836,7 @@ class JobConcurrencyManager {
* another job to run, or if system state suggests the job should stop.
*/
@Nullable
+ @GuardedBy("mLock")
String shouldStopRunningJobLocked(@NonNull JobServiceContext context) {
final JobStatus js = context.getRunningJobLocked();
if (js == null) {
@@ -784,17 +879,22 @@ class JobConcurrencyManager {
// Only expedited jobs can replace expedited jobs.
if (js.shouldTreatAsExpeditedJob()) {
// Keep fg/bg user distinction.
- if (workType == WORK_TYPE_BGUSER) {
- // For now, let any bg user job replace a bg user expedited job.
- // TODO: limit to ej once we have dedicated bg user ej slots.
- if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER) > 0) {
- return "blocking " + workTypeToString(workType) + " queue";
+ if (workType == WORK_TYPE_BGUSER_IMPORTANT || workType == WORK_TYPE_BGUSER) {
+ // Let any important bg user job replace a bg user expedited job.
+ if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER_IMPORTANT) > 0) {
+ return "blocking " + workTypeToString(WORK_TYPE_BGUSER_IMPORTANT) + " queue";
}
- } else {
- if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
- return "blocking " + workTypeToString(workType) + " queue";
+ // Let a fg user EJ preempt a bg user EJ (if able), but not the other way around.
+ if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0
+ && mWorkCountTracker.canJobStart(WORK_TYPE_EJ, workType)
+ != WORK_TYPE_NONE) {
+ return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
}
+ } else if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) {
+ return "blocking " + workTypeToString(WORK_TYPE_EJ) + " queue";
}
+ // No other pending EJs. Return null so we don't let regular jobs preempt an EJ.
+ return null;
}
// Easy check. If there are pending jobs of the same work type, then we know that
@@ -848,6 +948,7 @@ class JobConcurrencyManager {
return s.toString();
}
+ @GuardedBy("mLock")
void updateConfigLocked() {
DeviceConfig.Properties properties =
DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
@@ -977,10 +1078,12 @@ 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;
}
@@ -989,7 +1092,11 @@ class JobConcurrencyManager {
classification |= WORK_TYPE_EJ;
}
} else {
- // TODO(171305774): create dedicated slots for EJs of bg user
+ if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE
+ || js.shouldTreatAsExpeditedJob()) {
+ classification |= WORK_TYPE_BGUSER_IMPORTANT;
+ }
+ // BGUSER_IMPORTANT jobs can also run as BGUSER jobs, so not an 'else' here.
classification |= WORK_TYPE_BGUSER;
}
@@ -1001,15 +1108,21 @@ 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_MAX_BGUSER_IMPORTANT =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_important_";
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 =
CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
+ private static final String KEY_PREFIX_MIN_BGUSER_IMPORTANT =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_important_";
private final String mConfigIdentifier;
private int mMaxTotal;
@@ -1053,6 +1166,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))));
@@ -1061,6 +1178,10 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+ final int maxBgUserImp = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_BGUSER_IMPORTANT + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, maxBgUserImp);
final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
@@ -1074,6 +1195,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,
@@ -1086,6 +1213,11 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
mMinReservedSlots.put(WORK_TYPE_BG, minBg);
remaining -= minBg;
+ // Ensure bg user imp is in the range [0, min(maxBgUserImp, remaining)]
+ final int minBgUserImp = Math.max(0, Math.min(Math.min(maxBgUserImp, remaining),
+ properties.getInt(KEY_PREFIX_MIN_BGUSER_IMPORTANT + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT, 0))));
+ mMinReservedSlots.put(WORK_TYPE_BGUSER_IMPORTANT, minBgUserImp);
// Ensure bg user is in the range [0, min(maxBgUser, remaining)]
final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
@@ -1111,6 +1243,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))
@@ -1120,6 +1256,10 @@ class JobConcurrencyManager {
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
.println();
pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+ mMinReservedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
+ pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+ mMaxAllowedSlots.get(WORK_TYPE_BGUSER_IMPORTANT)).println();
+ pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
@@ -1205,6 +1345,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
@@ -1218,16 +1359,10 @@ class JobConcurrencyManager {
void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
- mConfigNumReservedSlots.put(WORK_TYPE_TOP,
- workTypeConfig.getMinReserved(WORK_TYPE_TOP));
- 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_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));
+ for (int workType = 1; workType < ALL_WORK_TYPES; workType <<= 1) {
+ mConfigNumReservedSlots.put(workType, workTypeConfig.getMinReserved(workType));
+ mConfigAbsoluteMaxSlots.put(workType, workTypeConfig.getMax(workType));
+ }
mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
@@ -1260,15 +1395,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 +1410,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 +1508,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..6802b6b6ee2d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -269,14 +269,14 @@ public final class JobServiceContext implements ServiceConnection {
try {
final int bindFlags;
if (job.shouldTreatAsExpeditedJob()) {
- // TODO(171305774): The job should run on the little cores. We'll probably need
- // 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()));
@@ -353,15 +353,10 @@ public final class JobServiceContext implements ServiceConnection {
/** Called externally when a job that was scheduled for execution should be cancelled. */
@GuardedBy("mLock")
- void cancelExecutingJobLocked(int reason, String debugReason) {
+ void cancelExecutingJobLocked(int reason, @NonNull String debugReason) {
doCancelLocked(reason, debugReason);
}
- @GuardedBy("mLock")
- void preemptExecutingJobLocked(@NonNull String reason) {
- doCancelLocked(JobParameters.REASON_PREEMPT, reason);
- }
-
int getPreferredUid() {
return mPreferredUid;
}
@@ -379,8 +374,8 @@ public final class JobServiceContext implements ServiceConnection {
}
boolean isWithinExecutionGuaranteeTime() {
- return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis
- < sElapsedRealtimeClock.millis();
+ return sElapsedRealtimeClock.millis()
+ < mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
}
@GuardedBy("mLock")
@@ -618,7 +613,7 @@ public final class JobServiceContext implements ServiceConnection {
}
@GuardedBy("mLock")
- private void doCancelLocked(int stopReasonCode, String debugReason) {
+ private void doCancelLocked(int stopReasonCode, @Nullable String debugReason) {
if (mVerb == VERB_FINISHED) {
if (DEBUG) {
Slog.d(TAG,
@@ -731,7 +726,7 @@ public final class JobServiceContext implements ServiceConnection {
* _ENDING -> No point in doing anything here, so we ignore.
*/
@GuardedBy("mLock")
- private void handleCancelLocked(String reason) {
+ private void handleCancelLocked(@Nullable String reason) {
if (JobSchedulerService.DEBUG) {
Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
+ VERB_STRINGS[mVerb]);
@@ -815,7 +810,7 @@ public final class JobServiceContext implements ServiceConnection {
* VERB_STOPPING.
*/
@GuardedBy("mLock")
- private void sendStopMessageLocked(String reason) {
+ private void sendStopMessageLocked(@Nullable String reason) {
removeOpTimeOutLocked();
if (mVerb != VERB_EXECUTING) {
Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
@@ -841,18 +836,19 @@ public final class JobServiceContext implements ServiceConnection {
* we want to clean up internally.
*/
@GuardedBy("mLock")
- private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
+ private void closeAndCleanupJobLocked(boolean reschedule, @Nullable String reason) {
final JobStatus completedJob;
if (mVerb == VERB_FINISHED) {
return;
}
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 +859,7 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.hasContentTriggerConstraint());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
- mParams.getStopReason());
+ stopReason);
} catch (RemoteException e) {
// Whatever.
}
@@ -882,11 +878,11 @@ 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);
}
- private void applyStoppedReasonLocked(String reason) {
+ private void applyStoppedReasonLocked(@Nullable String reason) {
if (reason != null && mStoppedReason == null) {
mStoppedReason = reason;
mStoppedTime = sElapsedRealtimeClock.millis();
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/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 685cf0dc7f77..906071fe4c7b 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -39,10 +39,12 @@ import java.util.Set;
for handling newer video codec format and media features.
<p>
- Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can
- support playback of all media formats. Apps that would like to request that media be transcoded
- into a more compatible format should declare their media capabilities in a media_capabilities
- .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example:
+ Android 12 introduces Compatible media transcoding feature. See
+ <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
+ Compatible media transcoding</a>. By default, Android assumes apps can support playback of all
+ media formats. Apps that would like to request that media be transcoded into a more compatible
+ format should declare their media capabilities in a media_capabilities.xml resource file and add it
+ as a property tag in the AndroidManifest.xml file. Here is a example:
<pre>
{@code
<media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
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/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index f85e30d38f86..9c044b5e632e 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -66,6 +66,7 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
"set-phone-acct-suggestion-component";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
+ private static final String COMMAND_SET_CALL_DIAGNOSTIC_SERVICE = "set-call-diagnostic-service";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
@@ -112,6 +113,7 @@ public final class Telecom extends BaseCommand {
+ "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
+ " <LABEL> <ADDRESS>\n"
+ "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
+ + "usage: telecom set-call-diagnostic-service <PACKAGE>\n"
+ "usage: telecom set-default-dialer <PACKAGE>\n"
+ "usage: telecom get-default-dialer\n"
+ "usage: telecom get-system-dialer\n"
@@ -131,6 +133,7 @@ public final class Telecom extends BaseCommand {
+ "telecom set-phone-account-disabled: Disables the given phone account, if it"
+ " has already been registered with telecom.\n"
+ "\n"
+ + "telecom set-call-diagnostic-service: overrides call diagnostic service.\n"
+ "telecom set-default-dialer: Sets the override default dialer to the given"
+ " component; this will override whatever the dialer role is set to.\n"
+ "\n"
@@ -206,6 +209,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
runSetTestPhoneAcctSuggestionComponent();
break;
+ case COMMAND_SET_CALL_DIAGNOSTIC_SERVICE:
+ runSetCallDiagnosticService();
+ break;
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
@@ -323,6 +329,13 @@ public final class Telecom extends BaseCommand {
mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
}
+ private void runSetCallDiagnosticService() throws RemoteException {
+ String packageName = nextArg();
+ if ("default".equals(packageName)) packageName = null;
+ mTelecomService.setTestCallDiagnosticService(packageName);
+ System.out.println("Success - " + packageName + " set as call diagnostic service.");
+ }
+
private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
final String componentName = nextArg();
mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
diff --git a/core/api/current.txt b/core/api/current.txt
index 9b4b3cb57430..310e95990b85 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1072,6 +1072,7 @@ package android {
field public static final int panelTextAppearance = 16842850; // 0x1010062
field public static final int parentActivityName = 16843687; // 0x10103a7
field @Deprecated public static final int password = 16843100; // 0x101015c
+ field public static final int passwordsActivity = 16844351; // 0x101063f
field public static final int path = 16842794; // 0x101002a
field public static final int pathAdvancedPattern = 16844320; // 0x1010620
field public static final int pathData = 16843781; // 0x1010405
@@ -6810,14 +6811,18 @@ package android.app {
public final class WallpaperColors implements android.os.Parcelable {
ctor public WallpaperColors(android.os.Parcel);
ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color);
+ ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color, int);
method public int describeContents();
method public static android.app.WallpaperColors fromBitmap(@NonNull android.graphics.Bitmap);
method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
+ method public int getColorHints();
method @NonNull public android.graphics.Color getPrimaryColor();
method @Nullable public android.graphics.Color getSecondaryColor();
method @Nullable public android.graphics.Color getTertiaryColor();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
+ field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2
}
public final class WallpaperInfo implements android.os.Parcelable {
@@ -8367,7 +8372,6 @@ package android.appwidget {
public class AppWidgetHostView extends android.widget.FrameLayout {
ctor public AppWidgetHostView(android.content.Context);
ctor public AppWidgetHostView(android.content.Context, int, int);
- method public void clearCurrentSize();
method public int getAppWidgetId();
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo();
method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect);
@@ -8377,13 +8381,12 @@ package android.appwidget {
method public void resetColorResources();
method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
method public void setColorResources(@NonNull android.util.SparseIntArray);
- method public void setCurrentSize(@NonNull android.graphics.PointF);
method public void setExecutor(java.util.concurrent.Executor);
method public void setOnLightBackground(boolean);
method public void updateAppWidget(android.widget.RemoteViews);
method public void updateAppWidgetOptions(android.os.Bundle);
method @Deprecated public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
- method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.graphics.PointF>);
+ method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.util.SizeF>);
}
public class AppWidgetManager {
@@ -9712,7 +9715,7 @@ package android.companion {
}
public final class CompanionDeviceManager {
- method public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
+ method @RequiresPermission(value=android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
method public void disassociate(@NonNull String);
method @NonNull public java.util.List<java.lang.String> getAssociations();
method public boolean hasNotificationAccess(android.content.ComponentName);
@@ -10322,6 +10325,7 @@ package android.content {
method @Deprecated public abstract void clearWallpaper() throws java.io.IOException;
method @NonNull public android.content.Context createAttributionContext(@Nullable String);
method public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration);
+ method @NonNull public android.content.Context createContext(@NonNull android.content.ContextParams);
method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Context createDeviceProtectedStorageContext();
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
@@ -10543,6 +10547,19 @@ package android.content {
field public static final String WINDOW_SERVICE = "window";
}
+ public final class ContextParams {
+ method @Nullable public String getAttributionTag();
+ method @Nullable public String getReceiverAttributionTag();
+ method @Nullable public String getReceiverPackage();
+ }
+
+ public static final class ContextParams.Builder {
+ ctor public ContextParams.Builder();
+ method @NonNull public android.content.ContextParams build();
+ method @NonNull public android.content.ContextParams.Builder setAttributionTag(@NonNull String);
+ method @NonNull public android.content.ContextParams.Builder setReceiverPackage(@NonNull String, @Nullable String);
+ }
+
public class ContextWrapper extends android.content.Context {
ctor public ContextWrapper(android.content.Context);
method protected void attachBaseContext(android.content.Context);
@@ -11764,6 +11781,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
@@ -12877,18 +12895,18 @@ package android.content.pm {
}
public class ShortcutManager {
- method public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
- method public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo);
+ method @WorkerThread public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+ method @WorkerThread public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo);
method public void disableShortcuts(@NonNull java.util.List<java.lang.String>);
method public void disableShortcuts(@NonNull java.util.List<java.lang.String>, CharSequence);
method public void enableShortcuts(@NonNull java.util.List<java.lang.String>);
- method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
method public int getIconMaxHeight();
method public int getIconMaxWidth();
- method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
method public int getMaxShortcutCountPerActivity();
- method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
- method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int);
+ method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int);
method public boolean isRateLimitingActive();
method public boolean isRequestPinShortcutSupported();
method public void pushDynamicShortcut(@NonNull android.content.pm.ShortcutInfo);
@@ -12896,10 +12914,10 @@ package android.content.pm {
method public void removeDynamicShortcuts(@NonNull java.util.List<java.lang.String>);
method public void removeLongLivedShortcuts(@NonNull java.util.List<java.lang.String>);
method public void reportShortcutUsed(String);
- method public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
- method public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+ method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
+ method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
- method public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+ method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
field public static final int FLAG_MATCH_CACHED = 8; // 0x8
field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
@@ -12943,6 +12961,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 {
@@ -17531,6 +17570,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
@@ -17742,6 +17784,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN;
@@ -18346,9 +18389,20 @@ package android.hardware.camera2 {
field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100
}
+ public class MultiResolutionImageReader implements java.lang.AutoCloseable {
+ method public void close();
+ method protected void finalize();
+ method public void flush();
+ method @NonNull public android.hardware.camera2.params.MultiResolutionStreamInfo getStreamInfoForImageReader(@NonNull android.media.ImageReader);
+ method @NonNull public android.view.Surface getSurface();
+ method @NonNull public static android.hardware.camera2.MultiResolutionImageReader newInstance(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
+ method public void setOnImageAvailableListener(@Nullable android.media.ImageReader.OnImageAvailableListener, @Nullable java.util.concurrent.Executor);
+ }
+
public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
method @NonNull public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults();
- method public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+ method @Deprecated public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults();
+ method @NonNull public java.util.Map<java.lang.String,android.hardware.camera2.TotalCaptureResult> getPhysicalCameraTotalResults();
}
}
@@ -18397,9 +18451,11 @@ package android.hardware.camera2.params {
public final class InputConfiguration {
ctor public InputConfiguration(int, int, int);
+ ctor public InputConfiguration(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int);
method public int getFormat();
method public int getHeight();
method public int getWidth();
+ method public boolean isMultiResolution();
}
public final class LensShadingMap {
@@ -18442,6 +18498,20 @@ package android.hardware.camera2.params {
field public static final int METERING_WEIGHT_MIN = 0; // 0x0
}
+ public final class MultiResolutionStreamConfigurationMap {
+ method @NonNull public int[] getInputFormats();
+ method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getInputInfo(int);
+ method @NonNull public int[] getOutputFormats();
+ method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getOutputInfo(int);
+ }
+
+ public class MultiResolutionStreamInfo {
+ ctor public MultiResolutionStreamInfo(int, int, @NonNull String);
+ method public int getHeight();
+ method @NonNull public String getPhysicalCameraId();
+ method public int getWidth();
+ }
+
public final class OisSample {
ctor public OisSample(long, float, float);
method public long getTimestamp();
@@ -18454,6 +18524,7 @@ package android.hardware.camera2.params {
ctor public OutputConfiguration(int, @NonNull android.view.Surface);
ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
method public void addSurface(@NonNull android.view.Surface);
+ method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
method public int describeContents();
method public void enableSurfaceSharing();
method public int getMaxSharedSurfaceCount();
@@ -18565,6 +18636,23 @@ package android.hardware.camera2.params {
package android.hardware.display {
+ public final class DeviceProductInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getConnectionToSinkType();
+ method public int getManufactureWeek();
+ method public int getManufactureYear();
+ method @NonNull public String getManufacturerPnpId();
+ method public int getModelYear();
+ method @Nullable public String getName();
+ method @NonNull public String getProductId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CONNECTION_TO_SINK_BUILT_IN = 1; // 0x1
+ field public static final int CONNECTION_TO_SINK_DIRECT = 2; // 0x2
+ field public static final int CONNECTION_TO_SINK_TRANSITIVE = 3; // 0x3
+ field public static final int CONNECTION_TO_SINK_UNKNOWN = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.DeviceProductInfo> CREATOR;
+ }
+
public final class DisplayManager {
method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int);
method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
@@ -20279,6 +20367,10 @@ package android.media {
field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_BIT_WIDTH;
field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_CHANNEL_MASK;
field @NonNull public static final android.media.AudioMetadata.Key<java.lang.String> KEY_MIME;
+ field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER;
+ field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_PRESENTATION_ID;
+ field @NonNull public static final android.media.AudioMetadata.Key<java.lang.String> KEY_PRESENTATION_LANGUAGE;
+ field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_PROGRAM_ID;
field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_SAMPLE_RATE;
}
@@ -20333,6 +20425,15 @@ package android.media {
method public boolean hasAudioDescription();
method public boolean hasDialogueEnhancement();
method public boolean hasSpokenSubtitles();
+ field public static final int CONTENT_COMMENTARY = 5; // 0x5
+ field public static final int CONTENT_DIALOG = 4; // 0x4
+ field public static final int CONTENT_EMERGENCY = 6; // 0x6
+ field public static final int CONTENT_HEARING_IMPAIRED = 3; // 0x3
+ field public static final int CONTENT_MAIN = 0; // 0x0
+ field public static final int CONTENT_MUSIC_AND_EFFECTS = 1; // 0x1
+ field public static final int CONTENT_UNKNOWN = -1; // 0xffffffff
+ field public static final int CONTENT_VISUALLY_IMPAIRED = 2; // 0x2
+ field public static final int CONTENT_VOICEOVER = 7; // 0x7
field public static final int MASTERED_FOR_3D = 3; // 0x3
field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
field public static final int MASTERED_FOR_STEREO = 1; // 0x1
@@ -25866,6 +25967,7 @@ package android.net {
method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
method public int getTruncationLengthBits();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String AUTH_AES_CMAC = "cmac(aes)";
field public static final String AUTH_AES_XCBC = "xcbc(aes)";
field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
@@ -26007,6 +26109,16 @@ package android.net {
field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
}
+ public final class Proxy {
+ ctor public Proxy();
+ method @Deprecated public static String getDefaultHost();
+ method @Deprecated public static int getDefaultPort();
+ method @Deprecated public static String getHost(android.content.Context);
+ method @Deprecated public static int getPort(android.content.Context);
+ field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+ field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+ }
+
@Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
ctor @Deprecated public SSLCertificateSocketFactory(int);
method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
@@ -26678,7 +26790,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);
}
}
@@ -30228,6 +30355,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;
@@ -30386,19 +30514,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();
@@ -31823,6 +31942,7 @@ package android.os.storage {
method @WorkerThread public long getAllocatableBytes(@NonNull java.util.UUID) throws java.io.IOException;
method @WorkerThread public long getCacheQuotaBytes(@NonNull java.util.UUID) throws java.io.IOException;
method @WorkerThread public long getCacheSizeBytes(@NonNull java.util.UUID) throws java.io.IOException;
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public android.app.PendingIntent getManageSpaceActivityIntent(@NonNull String, int);
method public String getMountedObbPath(String);
method @NonNull public android.os.storage.StorageVolume getPrimaryStorageVolume();
method @NonNull public java.util.List<android.os.storage.StorageVolume> getRecentStorageVolumes();
@@ -38948,6 +39068,10 @@ package android.telecom {
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
field @Deprecated public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+ field public static final String EVENT_CLEAR_DIAGNOSTIC_MESSAGE = "android.telecom.event.CLEAR_DIAGNOSTIC_MESSAGE";
+ field public static final String EVENT_DISPLAY_DIAGNOSTIC_MESSAGE = "android.telecom.event.DISPLAY_DIAGNOSTIC_MESSAGE";
+ field public static final String EXTRA_DIAGNOSTIC_MESSAGE = "android.telecom.extra.DIAGNOSTIC_MESSAGE";
+ field public static final String EXTRA_DIAGNOSTIC_MESSAGE_ID = "android.telecom.extra.DIAGNOSTIC_MESSAGE_ID";
field public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED";
field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
@@ -41312,7 +41436,6 @@ package android.telephony {
method @NonNull public java.util.List<java.lang.Integer> getAvailableServices();
method @Nullable public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
- method public int getNrState();
method @Nullable public String getRegisteredPlmn();
method public int getTransportType();
method public boolean isRegistered();
@@ -41431,29 +41554,29 @@ package android.telephony {
field public static final char WILD = 78; // 0x004e 'N'
}
- public class PhoneStateListener {
- ctor public PhoneStateListener();
+ @Deprecated public class PhoneStateListener {
+ ctor @Deprecated public PhoneStateListener();
ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
- method public void onActiveDataSubscriptionIdChanged(int);
- method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
- method public void onCallForwardingIndicatorChanged(boolean);
- method public void onCallStateChanged(int, String);
- method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
- method public void onCellLocationChanged(android.telephony.CellLocation);
- method public void onDataActivity(int);
- method public void onDataConnectionStateChanged(int);
- method public void onDataConnectionStateChanged(int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
- method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
- method public void onMessageWaitingIndicatorChanged(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
- method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
- method public void onServiceStateChanged(android.telephony.ServiceState);
+ method @Deprecated public void onActiveDataSubscriptionIdChanged(int);
+ method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+ method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
+ method @Deprecated public void onCallStateChanged(int, String);
+ method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
+ method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
+ method @Deprecated public void onDataActivity(int);
+ method @Deprecated public void onDataConnectionStateChanged(int);
+ method @Deprecated public void onDataConnectionStateChanged(int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+ method @Deprecated public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void onMessageWaitingIndicatorChanged(boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+ method @Deprecated public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+ method @Deprecated public void onServiceStateChanged(android.telephony.ServiceState);
method @Deprecated public void onSignalStrengthChanged(int);
- method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
- method public void onUserMobileDataStateChanged(boolean);
+ method @Deprecated public void onSignalStrengthsChanged(android.telephony.SignalStrength);
+ method @Deprecated public void onUserMobileDataStateChanged(boolean);
field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
@@ -41467,7 +41590,7 @@ package android.telephony {
field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
- field public static final int LISTEN_NONE = 0; // 0x0
+ field @Deprecated public static final int LISTEN_NONE = 0; // 0x0
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
@@ -41476,90 +41599,6 @@ package android.telephony {
field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
}
- public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
- }
-
- public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener {
- method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
- }
-
- public static interface PhoneStateListener.BarringInfoChangedListener {
- method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
- }
-
- public static interface PhoneStateListener.CallDisconnectCauseChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
- }
-
- public static interface PhoneStateListener.CallForwardingIndicatorChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
- }
-
- public static interface PhoneStateListener.CallStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
- }
-
- public static interface PhoneStateListener.CarrierNetworkChangeListener {
- method public void onCarrierNetworkChange(boolean);
- }
-
- public static interface PhoneStateListener.CellInfoChangedListener {
- method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
- }
-
- public static interface PhoneStateListener.CellLocationChangedListener {
- method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
- }
-
- public static interface PhoneStateListener.DataActivationStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
- }
-
- public static interface PhoneStateListener.DataActivityListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
- }
-
- public static interface PhoneStateListener.DataConnectionStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
- }
-
- public static interface PhoneStateListener.DisplayInfoChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
- }
-
- public static interface PhoneStateListener.EmergencyNumberListChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
- }
-
- public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
- }
-
- public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
- }
-
- public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
- }
-
- public static interface PhoneStateListener.RegistrationFailedListener {
- method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
- }
-
- public static interface PhoneStateListener.ServiceStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
- }
-
- public static interface PhoneStateListener.SignalStrengthsChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
- }
-
- public static interface PhoneStateListener.UserMobileDataStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
- }
-
public final class PhysicalChannelConfig implements android.os.Parcelable {
method public int describeContents();
method @IntRange(from=1, to=261) public int getBand();
@@ -42034,6 +42073,94 @@ package android.telephony {
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
+ public class TelephonyCallback {
+ ctor public TelephonyCallback();
+ }
+
+ public static interface TelephonyCallback.ActiveDataSubscriptionIdListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
+ }
+
+ public static interface TelephonyCallback.AlwaysReportedSignalStrengthListener {
+ method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+ }
+
+ public static interface TelephonyCallback.BarringInfoListener {
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+ }
+
+ public static interface TelephonyCallback.CallDisconnectCauseListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+ }
+
+ public static interface TelephonyCallback.CallForwardingIndicatorListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
+ }
+
+ public static interface TelephonyCallback.CallStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
+ }
+
+ public static interface TelephonyCallback.CarrierNetworkListener {
+ method public void onCarrierNetworkChange(boolean);
+ }
+
+ public static interface TelephonyCallback.CellInfoListener {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
+ }
+
+ public static interface TelephonyCallback.CellLocationListener {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
+ }
+
+ public static interface TelephonyCallback.DataActivationStateListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
+ }
+
+ public static interface TelephonyCallback.DataActivityListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
+ }
+
+ public static interface TelephonyCallback.DataConnectionStateListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
+ }
+
+ public static interface TelephonyCallback.DisplayInfoListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+ }
+
+ public static interface TelephonyCallback.EmergencyNumberListListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+ }
+
+ public static interface TelephonyCallback.ImsCallDisconnectCauseListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ }
+
+ public static interface TelephonyCallback.MessageWaitingIndicatorListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
+ }
+
+ public static interface TelephonyCallback.PreciseDataConnectionStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+ }
+
+ public static interface TelephonyCallback.RegistrationFailedListener {
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+ }
+
+ public static interface TelephonyCallback.ServiceStateListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
+ }
+
+ public static interface TelephonyCallback.SignalStrengthsListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+ }
+
+ public static interface TelephonyCallback.UserMobileDataStateListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
+ }
+
public final class TelephonyDisplayInfo implements android.os.Parcelable {
method public int describeContents();
method public int getNetworkType();
@@ -42146,7 +42273,7 @@ package android.telephony {
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
- method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener);
+ method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
@@ -42170,7 +42297,7 @@ package android.telephony {
method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
- method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
+ method public void unregisterTelephonyCallback(@NonNull android.telephony.TelephonyCallback);
method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
@@ -42199,7 +42326,6 @@ package android.telephony {
field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0
field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1
- field public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; // 0x2
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -45834,11 +45960,14 @@ package android.util {
method public static android.util.Size parseSize(String) throws java.lang.NumberFormatException;
}
- public final class SizeF {
+ public final class SizeF implements android.os.Parcelable {
ctor public SizeF(float, float);
+ method public int describeContents();
method public float getHeight();
method public float getWidth();
method public static android.util.SizeF parseSizeF(String) throws java.lang.NumberFormatException;
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.util.SizeF> CREATOR;
}
public class SparseArray<E> implements java.lang.Cloneable {
@@ -45848,7 +45977,7 @@ package android.util {
method public void clear();
method public android.util.SparseArray<E> clone();
method public boolean contains(int);
- method public boolean contentEquals(@Nullable android.util.SparseArray<E>);
+ method public boolean contentEquals(@Nullable android.util.SparseArray<?>);
method public int contentHashCode();
method public void delete(int);
method public E get(int);
@@ -46244,6 +46373,7 @@ package android.view {
method public long getAppVsyncOffsetNanos();
method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
method @Nullable public android.view.DisplayCutout getCutout();
+ method @Nullable public android.hardware.display.DeviceProductInfo getDeviceProductInfo();
method public int getDisplayId();
method public int getFlags();
method public android.view.Display.HdrCapabilities getHdrCapabilities();
@@ -49720,9 +49850,12 @@ package android.view {
}
public interface WindowManager extends android.view.ViewManager {
+ method public default void addCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
+ method public default boolean isCrossWindowBlurEnabled();
+ method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void removeViewImmediate(android.view.View);
}
@@ -49747,12 +49880,14 @@ package android.view {
method public final int copyFrom(android.view.WindowManager.LayoutParams);
method public String debug(String);
method public int describeContents();
+ method public int getBlurBehindRadius();
method public int getColorMode();
method public int getFitInsetsSides();
method public int getFitInsetsTypes();
method public final CharSequence getTitle();
method public boolean isFitInsetsIgnoringVisibility();
method public static boolean mayUseInputMethod(int);
+ method public void setBlurBehindRadius(@IntRange(from=0) int);
method public void setColorMode(int);
method public void setFitInsetsIgnoringVisibility(boolean);
method public void setFitInsetsSides(int);
@@ -49863,7 +49998,6 @@ package android.view {
field @Deprecated public static final int TYPE_TOAST = 2005; // 0x7d5
field public static final int TYPE_WALLPAPER = 2013; // 0x7dd
field public float alpha;
- field public int blurBehindRadius;
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
@@ -50028,7 +50162,7 @@ package android.view.accessibility {
method public boolean canOpenPopup();
method public int describeContents();
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String);
- method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String);
+ method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(@NonNull String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList();
@@ -50930,6 +51064,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
@@ -54724,7 +54859,7 @@ package android.widget {
public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
ctor public RemoteViews(String, int);
ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
- ctor public RemoteViews(@NonNull java.util.Map<android.graphics.PointF,android.widget.RemoteViews>);
+ ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
ctor public RemoteViews(android.widget.RemoteViews);
ctor public RemoteViews(android.os.Parcel);
method public void addView(@IdRes int, android.widget.RemoteViews);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7ea7d61ac3c5..ad2942a2079e 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -35,7 +35,7 @@ package android.app {
}
public final class PendingIntent implements android.os.Parcelable {
- method @Nullable @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
}
public class StatusBarManager {
@@ -44,6 +44,14 @@ package android.app {
}
+package android.app.usage {
+
+ public class NetworkStatsManager {
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>);
+ }
+
+}
+
package android.content {
public abstract class Context {
@@ -167,10 +175,26 @@ package android.net {
method public int getResourceId();
}
+ public final class NetworkStateSnapshot implements android.os.Parcelable {
+ ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
+ field public final int legacyType;
+ field @NonNull public final android.net.LinkProperties linkProperties;
+ field @NonNull public final android.net.Network network;
+ field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
+ field @Nullable public final String subscriberId;
+ }
+
public class NetworkWatchlistManager {
method @Nullable public byte[] getWatchlistConfigHash();
}
+ public final class Proxy {
+ method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+ }
+
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
@@ -217,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 3da6add3a04e..3a238e29dd23 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -32,6 +32,7 @@ package android {
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
+ field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
@@ -153,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";
@@ -356,6 +358,7 @@ package android {
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemShell = 17039402; // 0x104002a
field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e
+ field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
}
public static final class R.style {
@@ -422,6 +425,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";
@@ -535,9 +541,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);
@@ -564,6 +575,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);
@@ -857,13 +869,6 @@ package android.app {
method public void onVrStateChanged(boolean);
}
- public final class WallpaperColors implements android.os.Parcelable {
- ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color, int);
- method public int getColorHints();
- field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
- field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2
- }
-
public final class WallpaperInfo implements android.os.Parcelable {
method public boolean supportsAmbientMode();
}
@@ -961,6 +966,9 @@ package android.app.admin {
package android.app.assist {
+ public class ActivityId {
+ }
+
public static class AssistStructure.ViewNode {
ctor public AssistStructure.ViewNode();
}
@@ -1891,11 +1899,18 @@ package android.bluetooth {
field public static final int ACCESS_REJECTED = 2; // 0x2
field public static final int ACCESS_UNKNOWN = 0; // 0x0
field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
+ field public static final String DEVICE_TYPE_DEFAULT = "Default";
+ field public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
+ field public static final String DEVICE_TYPE_WATCH = "Watch";
field public static final int METADATA_COMPANION_APP = 4; // 0x4
+ field public static final int METADATA_DEVICE_TYPE = 17; // 0x11
field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
field public static final int METADATA_IS_UNTETHERED_HEADSET = 6; // 0x6
+ field public static final int METADATA_MAIN_BATTERY = 18; // 0x12
+ field public static final int METADATA_MAIN_CHARGING = 19; // 0x13
field public static final int METADATA_MAIN_ICON = 5; // 0x5
+ field public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20; // 0x14
field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0
field public static final int METADATA_MAX_LENGTH = 2048; // 0x800
field public static final int METADATA_MODEL_NAME = 1; // 0x1
@@ -1903,12 +1918,15 @@ package android.bluetooth {
field public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; // 0xc
field public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; // 0xf
field public static final int METADATA_UNTETHERED_CASE_ICON = 9; // 0x9
+ field public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23; // 0x17
field public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; // 0xa
field public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; // 0xd
field public static final int METADATA_UNTETHERED_LEFT_ICON = 7; // 0x7
+ field public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21; // 0x15
field public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; // 0xb
field public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; // 0xe
field public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; // 0x8
+ field public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22; // 0x16
}
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -2556,9 +2574,7 @@ package android.content.pm {
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
- field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
- field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
- field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
+ field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
@@ -2687,7 +2703,7 @@ package android.content.pm {
}
public class ShortcutManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) @WorkerThread public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
method public boolean hasShareTargets(@NonNull String);
}
@@ -2774,13 +2790,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;
@@ -2797,18 +2812,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
}
}
@@ -3445,6 +3450,10 @@ package android.hardware.lights {
field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
}
+ public static final class LightsRequest.Builder {
+ method @Deprecated @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ }
+
}
package android.hardware.location {
@@ -5087,9 +5096,10 @@ package android.media {
}
public static class AudioTrack.TunerConfiguration {
- ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=1) int, @IntRange(from=1) int);
+ ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=0) int, @IntRange(from=1) int);
method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getContentId();
method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getSyncId();
+ field public static final int CONTENT_ID_NONE = 0; // 0x0
}
public class HwAudioSource {
@@ -7155,9 +7165,6 @@ package android.net {
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
- public class NetworkReleasedException extends java.lang.Exception {
- }
-
public class NetworkScoreManager {
method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
@@ -7241,47 +7248,6 @@ package android.net {
method @NonNull public android.net.OemNetworkPreferences.Builder clearNetworkPreference(@NonNull String);
}
- public abstract class QosCallback {
- ctor public QosCallback();
- method public void onError(@NonNull android.net.QosCallbackException);
- method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes);
- method public void onQosSessionLost(@NonNull android.net.QosSession);
- }
-
- public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException {
- }
-
- public final class QosCallbackException extends java.lang.Exception {
- }
-
- public abstract class QosFilter {
- method @NonNull public abstract android.net.Network getNetwork();
- method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
- }
-
- public final class QosSession implements android.os.Parcelable {
- ctor public QosSession(int, int);
- method public int describeContents();
- method public int getSessionId();
- method public int getSessionType();
- method public long getUniqueId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
- field public static final int TYPE_EPS_BEARER = 1; // 0x1
- }
-
- public interface QosSessionAttributes {
- }
-
- public final class QosSocketInfo implements android.os.Parcelable {
- ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
- method public int describeContents();
- method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
- method @NonNull public android.net.Network getNetwork();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
- }
-
public class RssiCurve implements android.os.Parcelable {
ctor public RssiCurve(int, int, byte[]);
ctor public RssiCurve(int, int, byte[], int);
@@ -7313,12 +7279,6 @@ package android.net {
field public final android.net.RssiCurve rssiCurve;
}
- public class SocketLocalAddressChangedException extends java.lang.Exception {
- }
-
- public class SocketNotBoundException extends java.lang.Exception {
- }
-
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
@@ -7548,6 +7508,19 @@ package android.net.sip {
}
+package android.net.util {
+
+ public final class SocketUtils {
+ method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
+ method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+ method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
+ method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
+ method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
+ method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
+ }
+
+}
+
package android.net.vcn {
public class VcnManager {
@@ -8246,10 +8219,11 @@ package android.os {
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
- field public static final int REASON_ACTIVITY_RECOGNITION = 102; // 0x66
+ field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
field public static final int REASON_GEOFENCING = 100; // 0x64
field public static final int REASON_OTHER = 1; // 0x1
field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+ field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
field public static final int REASON_UNKNOWN = 0; // 0x0
field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
@@ -8865,6 +8839,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";
@@ -10380,6 +10356,16 @@ package android.telecom {
ctor @Deprecated public Call.Listener();
}
+ public abstract class CallDiagnosticService extends android.app.Service {
+ ctor public CallDiagnosticService();
+ method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport);
+ method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
+ method @NonNull public abstract android.telecom.DiagnosticCall onInitializeDiagnosticCall(@NonNull android.telecom.Call.Details);
+ method public abstract void onRemoveDiagnosticCall(@NonNull android.telecom.DiagnosticCall);
+ field public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService";
+ }
+
public static class CallScreeningService.CallResponse.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean);
}
@@ -10412,6 +10398,9 @@ package android.telecom {
method public void setTelecomCallId(@NonNull String);
field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
+ field public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE = "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+ field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
+ field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1; // 0x1
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
@@ -10437,6 +10426,34 @@ package android.telecom {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
+ public abstract class DiagnosticCall {
+ ctor public DiagnosticCall();
+ method public final void clearDiagnosticMessage(int);
+ method public final void displayDiagnosticMessage(int, @NonNull CharSequence);
+ method @NonNull public android.telecom.Call.Details getCallDetails();
+ method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details);
+ method @Nullable public abstract CharSequence onCallDisconnected(int, int);
+ method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo);
+ method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality);
+ method public abstract void onReceiveDeviceToDeviceMessage(int, int);
+ method public final void sendDeviceToDeviceMessage(int, int);
+ field public static final int AUDIO_CODEC_AMR_NB = 3; // 0x3
+ field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
+ field public static final int AUDIO_CODEC_EVS = 1; // 0x1
+ field public static final int BATTERY_STATE_CHARGING = 3; // 0x3
+ field public static final int BATTERY_STATE_GOOD = 2; // 0x2
+ field public static final int BATTERY_STATE_LOW = 1; // 0x1
+ field public static final int COVERAGE_GOOD = 2; // 0x2
+ field public static final int COVERAGE_POOR = 1; // 0x1
+ field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2
+ field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1
+ field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3
+ field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4
+ field public static final int NETWORK_TYPE_IWLAN = 2; // 0x2
+ field public static final int NETWORK_TYPE_LTE = 1; // 0x1
+ field public static final int NETWORK_TYPE_NR = 3; // 0x3
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -11060,51 +11077,16 @@ package android.telephony {
method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
}
- public class PhoneStateListener {
- method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+ @Deprecated public class PhoneStateListener {
+ method @Deprecated public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
- method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
- method public void onRadioPowerStateChanged(int);
- method public void onSrvccStateChanged(int);
- method public void onVoiceActivationStateChanged(int);
- field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
- field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
- field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
- field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
- field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
- field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
- field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
- field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
- field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
- field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
- field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
- field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
- field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
- field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
- field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
- field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
- field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
- field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
- field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
- field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
- field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+ method @Deprecated public void onRadioPowerStateChanged(int);
+ method @Deprecated public void onSrvccStateChanged(int);
+ method @Deprecated public void onVoiceActivationStateChanged(int);
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11114,50 +11096,6 @@ package android.telephony {
field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
}
- public static interface PhoneStateListener.AllowedNetworkTypesChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
- }
-
- public static interface PhoneStateListener.CallAttributesChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
- }
-
- public static interface PhoneStateListener.DataEnabledChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
- }
-
- public static interface PhoneStateListener.OutgoingEmergencyCallListener {
- method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- }
-
- public static interface PhoneStateListener.OutgoingEmergencySmsListener {
- method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
- }
-
- public static interface PhoneStateListener.PhoneCapabilityChangedListener {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
- }
-
- public static interface PhoneStateListener.PhysicalChannelConfigChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
- }
-
- public static interface PhoneStateListener.PreciseCallStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
- }
-
- public static interface PhoneStateListener.RadioPowerStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
- }
-
- public static interface PhoneStateListener.SrvccStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
- }
-
- public static interface PhoneStateListener.VoiceActivationStateChangedListener {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
- }
-
public final class PinResult implements android.os.Parcelable {
method public int describeContents();
method public int getAttemptsRemaining();
@@ -11495,6 +11433,88 @@ package android.telephony {
method @Deprecated public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
}
+ public class TelephonyCallback {
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
+ field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
+ field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
+ field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
+ field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+ field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
+ field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
+ field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
+ field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
+ field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
+ field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
+ field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
+ field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
+ field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
+ field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
+ field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
+ field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
+ }
+
+ public static interface TelephonyCallback.AllowedNetworkTypesListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+ }
+
+ public static interface TelephonyCallback.CallAttributesListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+ }
+
+ public static interface TelephonyCallback.DataEnabledListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
+ }
+
+ public static interface TelephonyCallback.OutgoingEmergencyCallListener {
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ }
+
+ public static interface TelephonyCallback.OutgoingEmergencySmsListener {
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ }
+
+ public static interface TelephonyCallback.PhoneCapabilityListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+ }
+
+ public static interface TelephonyCallback.PhysicalChannelConfigListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+ }
+
+ public static interface TelephonyCallback.PreciseCallStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+ }
+
+ public static interface TelephonyCallback.RadioPowerStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
+ }
+
+ public static interface TelephonyCallback.SrvccStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
+ }
+
+ public static interface TelephonyCallback.VoiceActivationStateListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
+ }
+
public final class TelephonyHistogram implements android.os.Parcelable {
ctor public TelephonyHistogram(int, int, int);
ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);
@@ -13038,7 +13058,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";
@@ -13065,7 +13085,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 {
@@ -13121,7 +13141,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
@@ -13284,10 +13304,12 @@ package android.telephony.ims {
public final class SipMessage implements android.os.Parcelable {
ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
method public int describeContents();
+ method @Nullable public String getCallIdParameter();
method @NonNull public byte[] getContent();
method @NonNull public byte[] getEncodedMessage();
method @NonNull public String getHeaderSection();
method @NonNull public String getStartLine();
+ method @Nullable public String getViaBranchParameter();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
}
@@ -13597,7 +13619,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
@@ -14003,6 +14025,7 @@ package android.view.contentcapture {
public final class ContentCaptureContext implements android.os.Parcelable {
method @Nullable public android.content.ComponentName getActivityComponent();
+ method @Nullable public android.app.assist.ActivityId getActivityId();
method public int getDisplayId();
method public int getFlags();
method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
@@ -14065,9 +14088,13 @@ package android.view.translation {
public final class UiTranslationManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(@NonNull android.app.assist.ActivityId);
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(@NonNull android.app.assist.ActivityId);
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(@NonNull android.app.assist.ActivityId);
method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, @NonNull android.app.assist.ActivityId);
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6ee57d6b0392..b41f9702c7c1 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);
@@ -294,7 +298,7 @@ package android.app {
}
public final class PendingIntent implements android.os.Parcelable {
- method @Nullable @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+ method @NonNull @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
}
@@ -399,6 +403,8 @@ package android.app.admin {
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
method public void forceUpdateUserSetupComplete();
+ method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
+ method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
method public long getLastBugReportRequestTime();
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
@@ -544,6 +550,15 @@ package android.app.admin {
}
+package android.app.assist {
+
+ public class ActivityId {
+ method public int getTaskId();
+ method @Nullable public android.os.IBinder getToken();
+ }
+
+}
+
package android.app.blob {
public class BlobStoreManager {
@@ -1474,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 {
@@ -1491,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/Activity.java b/core/java/android/app/Activity.java
index a73fe71cfe1a..f5f0b422a69d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -803,6 +803,7 @@ public class Activity extends ContextThemeWrapper
@UnsupportedAppUsage
private IBinder mToken;
private IBinder mAssistToken;
+ private IBinder mShareableActivityToken;
@UnsupportedAppUsage
private int mIdent;
@UnsupportedAppUsage
@@ -1210,7 +1211,7 @@ public class Activity extends ContextThemeWrapper
if (window != null) {
cm.updateWindowAttributes(window.getAttributes());
}
- cm.onActivityCreated(mToken, getComponentName());
+ cm.onActivityCreated(mToken, mShareableActivityToken, getComponentName());
break;
case CONTENT_CAPTURE_RESUME:
cm.onActivityResumed();
@@ -7118,6 +7119,9 @@ public class Activity extends ContextThemeWrapper
case "--contentcapture":
dumpContentCaptureManager(prefix, writer);
return;
+ case "--translation":
+ dumpUiTranslation(prefix, writer);
+ return;
}
}
writer.print(prefix); writer.print("Local Activity ");
@@ -7158,6 +7162,7 @@ public class Activity extends ContextThemeWrapper
dumpAutofillManager(prefix, writer);
dumpContentCaptureManager(prefix, writer);
+ dumpUiTranslation(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
}
@@ -7182,6 +7187,14 @@ public class Activity extends ContextThemeWrapper
}
}
+ void dumpUiTranslation(String prefix, PrintWriter writer) {
+ if (mUiTranslationController != null) {
+ mUiTranslationController.dump(prefix, writer);
+ } else {
+ writer.print(prefix); writer.println("No UiTranslationController");
+ }
+ }
+
/**
* Bit indicating that this activity is "immersive" and should not be
* interrupted by notifications if possible.
@@ -7838,7 +7851,8 @@ public class Activity extends ContextThemeWrapper
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
- Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
+ Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
+ IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
@@ -7860,6 +7874,7 @@ public class Activity extends ContextThemeWrapper
mInstrumentation = instr;
mToken = token;
mAssistToken = assistToken;
+ mShareableActivityToken = shareableActivityToken;
mIdent = ident;
mApplication = application;
mIntent = intent;
@@ -7918,6 +7933,11 @@ public class Activity extends ContextThemeWrapper
}
/** @hide */
+ public final IBinder getShareableActivityToken() {
+ return mParent != null ? mParent.getShareableActivityToken() : mShareableActivityToken;
+ }
+
+ /** @hide */
@VisibleForTesting
public final ActivityThread getActivityThread() {
return mMainThread;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 47d2e7cee65a..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,11 +514,16 @@ 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
public IBinder token;
public IBinder assistToken;
+ // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
+ // used without security checks
+ public IBinder shareableActivityToken;
int ident;
@UnsupportedAppUsage
Intent intent;
@@ -604,9 +611,11 @@ public final class ActivityThread extends ClientTransactionHandler {
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
- IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) {
+ IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
+ IBinder shareableActivityToken) {
this.token = token;
this.assistToken = assistToken;
+ this.shareableActivityToken = shareableActivityToken;
this.ident = ident;
this.intent = intent;
this.referrer = referrer;
@@ -1934,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;
@@ -1983,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";
@@ -2175,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;
@@ -3157,11 +3172,13 @@ public final class ActivityThread extends ClientTransactionHandler {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final Activity startActivityNow(Activity parent, String id,
- Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
- Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken) {
+ Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
+ Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken,
+ IBinder shareableActivityToken) {
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.assistToken = assistToken;
+ r.shareableActivityToken = shareableActivityToken;
r.ident = 0;
r.intent = intent;
r.state = state;
@@ -3504,7 +3521,7 @@ public final class ActivityThread extends ClientTransactionHandler {
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
- r.assistToken);
+ r.assistToken, r.shareableActivityToken);
if (customIntent != null) {
activity.mIntent = customIntent;
@@ -6788,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;
@@ -6849,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/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bc798136f2e3..fd56c449012f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -24,6 +24,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContextParams;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -2606,6 +2607,12 @@ class ContextImpl extends Context {
compatInfo, mClassLoader, loaders);
}
+ @NonNull
+ @Override
+ public Context createContext(@NonNull ContextParams contextParams) {
+ return this;
+ }
+
@Override
public @NonNull Context createAttributionContext(@Nullable String attributionTag) {
return new ContextImpl(this, mMainThread, mPackageInfo, attributionTag, mSplitName,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3a8172ea98b8..ef0dcabbe111 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -706,7 +706,7 @@ interface IActivityManager {
boolean stopProfile(int userId);
/** Called by PendingIntent.queryIntentComponents() */
- List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
+ ParceledListSlice queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags);
int getUidProcessCapabilities(int uid, in String callingPackage);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8d1076ea1277..b2184fe65887 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1247,7 +1247,8 @@ public class Instrumentation {
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
- null /* window */, null /* activityConfigCallback */, null /*assistToken*/);
+ null /* window */, null /* activityConfigCallback */, null /*assistToken*/,
+ null /*shareableActivityToken*/);
return activity;
}
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 74e61250f109..6b5f19a52e33 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -158,7 +158,7 @@ public class LocalActivityManager {
r.activityInfo = mActivityThread.resolveActivityInfo(r.intent);
}
r.activity = mActivityThread.startActivityNow(
- mParent, r.id, r.intent, r.activityInfo, r, r.instanceState, instance, r);
+ mParent, r.id, r.intent, r.activityInfo, r, r.instanceState, instance, r, r);
if (r.activity == null) {
return;
}
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/OWNERS b/core/java/android/app/OWNERS
index 1ff64dbe6d2e..e0e9b62d3809 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -73,6 +73,7 @@ per-file ClientTransactionHandler.java = file:/services/core/java/com/android/se
per-file Fragment.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS
per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file ConfigurationController.java = file:/services/core/java/com/android/server/wm/OWNERS
# TODO(b/174932174): determine the ownership of KeyguardManager.java
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 549bd4b9fe6a..009c9366b1d6 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -26,7 +26,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
import android.annotation.TestApi;
@@ -41,6 +40,7 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
@@ -60,6 +60,7 @@ import com.android.internal.os.IResultReceiver;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -1239,14 +1240,17 @@ public final class PendingIntent implements Parcelable {
* @param flags MATCH_* flags from {@link android.content.pm.PackageManager}.
* @hide
*/
- @SuppressLint("NullableCollection")
@RequiresPermission(permission.GET_INTENT_SENDER_INTENT)
@SystemApi(client = Client.MODULE_LIBRARIES)
@TestApi
- public @Nullable List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
+ public @NonNull List<ResolveInfo> queryIntentComponents(@ResolveInfoFlags int flags) {
try {
- return ActivityManager.getService()
+ ParceledListSlice<ResolveInfo> parceledList = ActivityManager.getService()
.queryIntentComponentsForIntentSender(mTarget, flags);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index ea7eab2a2877..358ce6a83a21 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -202,7 +202,7 @@ public final class PictureInPictureParams implements Parcelable {
}
if (in.readInt() != 0) {
mUserActions = new ArrayList<>();
- in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
+ in.readTypedList(mUserActions, RemoteAction.CREATOR);
}
if (in.readInt() != 0) {
mSourceRectHint = Rect.CREATOR.createFromParcel(in);
@@ -386,7 +386,7 @@ public final class PictureInPictureParams implements Parcelable {
}
if (mUserActions != null) {
out.writeInt(1);
- out.writeParcelableList(mUserActions, 0);
+ out.writeTypedList(mUserActions, 0);
} else {
out.writeInt(0);
}
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/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 3abba43ae0a8..0a8a73404a7b 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -16,9 +16,9 @@
package android.app;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -35,6 +35,8 @@ import com.android.internal.graphics.palette.Palette;
import com.android.internal.util.ContrastColorUtil;
import java.io.FileOutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -50,6 +52,13 @@ import java.util.stream.Collectors;
* or {@link WallpaperColors#getTertiaryColor()}.
*/
public final class WallpaperColors implements Parcelable {
+ /**
+ * @hide
+ */
+ @IntDef(prefix = "HINT_", value = {HINT_SUPPORTS_DARK_TEXT, HINT_SUPPORTS_DARK_THEME},
+ flag = true)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ColorsHints {}
private static final boolean DEBUG_DARK_PIXELS = false;
@@ -57,18 +66,14 @@ public final class WallpaperColors implements Parcelable {
* Specifies that dark text is preferred over the current wallpaper for best presentation.
* <p>
* eg. A launcher may set its text color to black if this flag is specified.
- * @hide
*/
- @SystemApi
public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
/**
* Specifies that dark theme is preferred over the current wallpaper for best presentation.
* <p>
* eg. A launcher may set its drawer color to black if this flag is specified.
- * @hide
*/
- @SystemApi
public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
/**
@@ -229,15 +234,12 @@ public final class WallpaperColors implements Parcelable {
* @param primaryColor Primary color.
* @param secondaryColor Secondary color.
* @param tertiaryColor Tertiary color.
- * @param colorHints A combination of WallpaperColor hints.
- * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @param colorHints A combination of color hints.
* @see WallpaperColors#fromBitmap(Bitmap)
* @see WallpaperColors#fromDrawable(Drawable)
- * @hide
*/
- @SystemApi
public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
- @Nullable Color tertiaryColor, int colorHints) {
+ @Nullable Color tertiaryColor, @ColorsHints int colorHints) {
if (primaryColor == null) {
throw new IllegalArgumentException("Primary color should never be null.");
@@ -268,13 +270,14 @@ public final class WallpaperColors implements Parcelable {
*
* @param populationByColor Map with keys of colors, and value representing the number of
* occurrences of color in the wallpaper.
- * @param colorHints A combination of WallpaperColor hints.
+ * @param colorHints A combination of color hints.
* @hide
* @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
* @see WallpaperColors#fromBitmap(Bitmap)
* @see WallpaperColors#fromDrawable(Drawable)
*/
- public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor, int colorHints) {
+ public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor,
+ @ColorsHints int colorHints) {
mAllColors = populationByColor;
ArrayList<Map.Entry<Integer, Integer>> mapEntries = new ArrayList(
@@ -386,27 +389,14 @@ public final class WallpaperColors implements Parcelable {
}
/**
- * Combination of WallpaperColor hints.
- *
- * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
- * @return True if dark text is supported.
- * @hide
+ * Returns the color hints for this instance.
+ * @return The color hints.
*/
- @SystemApi
- public int getColorHints() {
+ public @ColorsHints int getColorHints() {
return mColorHints;
}
/**
- * @param colorHints Combination of WallpaperColors hints.
- * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
- * @hide
- */
- public void setColorHints(int colorHints) {
- mColorHints = colorHints;
- }
-
- /**
* Checks if image is bright and clean enough to support light text.
*
* @param source What to read.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index bb1ff6051d56..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 {
@@ -12239,8 +12240,9 @@ public class DevicePolicyManager {
*
* @hide
*/
- public Set<String> getDisallowedSystemApps(ComponentName admin, int userId,
- String provisioningAction) {
+ @TestApi
+ public @NonNull Set<String> getDisallowedSystemApps(@NonNull ComponentName admin,
+ @UserIdInt int userId, @NonNull String provisioningAction) {
try {
return new ArraySet<>(
mService.getDisallowedSystemApps(admin, userId, provisioningAction));
@@ -13004,6 +13006,7 @@ public class DevicePolicyManager {
*
* @hide
*/
+ @TestApi
public @NonNull Set<String> getDefaultCrossProfilePackages() {
throwIfParentInstance("getDefaultCrossProfilePackages");
if (mService != null) {
diff --git a/core/java/android/app/assist/ActivityId.java b/core/java/android/app/assist/ActivityId.java
new file mode 100644
index 000000000000..fb0d056c2a34
--- /dev/null
+++ b/core/java/android/app/assist/ActivityId.java
@@ -0,0 +1,125 @@
+/*
+ * 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.app.assist;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.service.contentcapture.ContentCaptureService;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.translation.UiTranslationManager;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * The class is used to identify an instance of an Activity. The system provides this to services
+ * that need to request operations on a specific Activity. For example, the system provides this in
+ * {@link ContentCaptureContext} to {@link ContentCaptureService} which can use it to issue requests
+ * like {@link UiTranslationManager#startTranslation}.
+ *
+ * @hide
+ */
+@Immutable
+@SystemApi
+public class ActivityId {
+
+ /**
+ * The identifier of the task this activity is in.
+ */
+ private final int mTaskId;
+ /**
+ * The identifier of the activity.
+ */
+ @Nullable
+ private final IBinder mActivityId;
+
+ /**
+ * @hide
+ */
+ public ActivityId(int taskId, @Nullable IBinder activityId) {
+ mTaskId = taskId;
+ mActivityId = activityId;
+ }
+
+ /**
+ * @hide
+ */
+ public ActivityId(@NonNull Parcel source) {
+ mTaskId = source.readInt();
+ mActivityId = source.readStrongBinder();
+ }
+
+ /**
+ * The identifier of the task this activity is in.
+ * @hide
+ */
+ @TestApi
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * The identifier of the activity. In some case, this value may be null, e.g. the child session
+ * of content capture.
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ public IBinder getToken() {
+ return mActivityId;
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(@NonNull Parcel dest, int parcelableFlags) {
+ dest.writeInt(mTaskId);
+ dest.writeStrongBinder(mActivityId);
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityId { taskId = " + mTaskId + ", activityId = " + mActivityId + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ActivityId that = (ActivityId) o;
+ if (mTaskId != that.mTaskId) {
+ return false;
+ }
+ return mActivityId != null
+ ? mActivityId.equals(that.mActivityId)
+ : that.mActivityId == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mTaskId;
+ result = 31 * result + (mActivityId != null ? mActivityId.hashCode() : 0);
+ return result;
+ }
+}
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/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 73a9ceccecee..94ab0dd00113 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -71,6 +71,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
private boolean mIsForward;
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
+ private IBinder mShareableActivityToken;
/**
* It is only non-null if the process is the first time to launch activity. It is only an
* optimization for quick look up of the interface so the field is ignored for comparison.
@@ -95,7 +96,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
- client, mAssistToken, mFixedRotationAdjustments);
+ client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -119,7 +120,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments) {
+ FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
instance = new LaunchActivityItem();
@@ -127,7 +128,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
voiceInteractor, procState, state, persistentState, pendingResults,
pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
- activityClientController, fixedRotationAdjustments);
+ activityClientController, fixedRotationAdjustments, shareableActivityToken);
return instance;
}
@@ -135,7 +136,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Override
public void recycle() {
setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
- null, false, null, null, null, null);
+ null, false, null, null, null, null, null);
ObjectPool.recycle(this);
}
@@ -164,6 +165,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
dest.writeStrongBinder(mAssistToken);
dest.writeStrongInterface(mActivityClientController);
dest.writeTypedObject(mFixedRotationAdjustments, flags);
+ dest.writeStrongBinder(mShareableActivityToken);
}
/** Read from Parcel. */
@@ -181,7 +183,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
- in.readTypedObject(FixedRotationAdjustments.CREATOR));
+ in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder());
}
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
@@ -219,7 +221,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
&& mIsForward == other.mIsForward
&& Objects.equals(mProfilerInfo, other.mProfilerInfo)
&& Objects.equals(mAssistToken, other.mAssistToken)
- && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments);
+ && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
+ && Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
}
@Override
@@ -241,6 +244,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
result = 31 * result + Objects.hashCode(mProfilerInfo);
result = 31 * result + Objects.hashCode(mAssistToken);
result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
+ result = 31 * result + Objects.hashCode(mShareableActivityToken);
return result;
}
@@ -277,7 +281,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
+ ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
+ ",pendingNewIntents=" + mPendingNewIntents + ",options=" + mActivityOptions
+ ",profilerInfo=" + mProfilerInfo + ",assistToken=" + mAssistToken
- + ",rotationAdj=" + mFixedRotationAdjustments + "}";
+ + ",rotationAdj=" + mFixedRotationAdjustments
+ + ",shareableActivityToken=" + mShareableActivityToken + "}";
}
// Using the same method to set and clear values to make sure we don't forget anything
@@ -288,7 +293,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
IBinder assistToken, IActivityClientController activityClientController,
- FixedRotationAdjustments fixedRotationAdjustments) {
+ FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken) {
instance.mIntent = intent;
instance.mIdent = ident;
instance.mInfo = info;
@@ -308,5 +313,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
instance.mAssistToken = assistToken;
instance.mActivityClientController = activityClientController;
instance.mFixedRotationAdjustments = fixedRotationAdjustments;
+ instance.mShareableActivityToken = shareableActivityToken;
}
}
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1d5dc1d5df1c..098d8b6c6058 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -16,6 +16,8 @@
package android.app.usage;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,8 +30,11 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
+import android.net.Network;
import android.net.NetworkStack;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
+import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Binder;
@@ -48,6 +53,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.util.List;
import java.util.Objects;
/**
@@ -633,6 +639,50 @@ public class NetworkStatsManager {
return template;
}
+ /**
+ * Notify {@code NetworkStatsService} about network status changed.
+ *
+ * Notifies NetworkStatsService of network state changes for data usage accounting purposes.
+ *
+ * To avoid races that attribute data usage to wrong network, such as new network with
+ * the same interface after SIM hot-swap, this function will not return until
+ * {@code NetworkStatsService} finishes its work of retrieving traffic statistics from
+ * all data sources.
+ *
+ * @param defaultNetworks the list of all networks that could be used by network traffic that
+ * does not explicitly select a network.
+ * @param networkStateSnapshots a list of {@link NetworkStateSnapshot}s, one for
+ * each network that is currently connected.
+ * @param activeIface the active (i.e., connected) default network interface for the calling
+ * uid. Used to determine on which network future calls to
+ * {@link android.net.TrafficStats#incrementOperationCount} applies to.
+ * @param underlyingNetworkInfos the list of underlying network information for all
+ * currently-connected VPNs.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void notifyNetworkStatus(
+ @NonNull List<Network> defaultNetworks,
+ @NonNull List<NetworkStateSnapshot> networkStateSnapshots,
+ @Nullable String activeIface,
+ @NonNull List<UnderlyingNetworkInfo> underlyingNetworkInfos) {
+ try {
+ Objects.requireNonNull(defaultNetworks);
+ Objects.requireNonNull(networkStateSnapshots);
+ Objects.requireNonNull(underlyingNetworkInfos);
+ // TODO: Change internal namings after the name is decided.
+ mService.forceUpdateIfaces(defaultNetworks.toArray(new Network[0]),
+ networkStateSnapshots.toArray(new NetworkStateSnapshot[0]), activeIface,
+ underlyingNetworkInfos.toArray(new UnderlyingNetworkInfo[0]));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static class CallbackHandler extends Handler {
private final int mNetworkType;
private final String mSubscriberId;
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/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index fc54c716d4ec..d79fac58cf12 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -39,6 +39,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.util.SizeF;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Gravity;
@@ -56,7 +57,6 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -93,7 +93,7 @@ public class AppWidgetHostView extends FrameLayout {
int mLayoutId = -1;
private InteractionHandler mInteractionHandler;
private boolean mOnLightBackground;
- private PointF mCurrentSize = null;
+ private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
@@ -253,9 +253,33 @@ public class AppWidgetHostView extends FrameLayout {
}
}
+ private SizeF computeSizeFromLayout(int left, int top, int right, int bottom) {
+ Rect padding = getDefaultPadding();
+ float density = getResources().getDisplayMetrics().density;
+ return new SizeF(
+ (right - left - padding.right - padding.left) / density,
+ (bottom - top - padding.bottom - padding.top) / density
+ );
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
+ SizeF oldSize = mCurrentSize;
+ SizeF newSize = computeSizeFromLayout(left, top, right, bottom);
+ mCurrentSize = newSize;
+ if (mLastInflatedRemoteViews != null) {
+ RemoteViews toApply = mLastInflatedRemoteViews.getRemoteViewsToApplyIfDifferent(
+ oldSize, newSize);
+ if (toApply != null) {
+ applyRemoteViews(toApply, false);
+ measureChildWithMargins(mView,
+ MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+ 0 /* widthUsed */,
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY),
+ 0 /* heightUsed */);
+ }
+ }
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e);
@@ -309,7 +333,7 @@ public class AppWidgetHostView extends FrameLayout {
* again. Typically, this will be size of the widget in landscape and portrait.
* On some foldables, this might include the size on the outer and inner screens.
*/
- public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<PointF> sizes) {
+ public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<SizeF> sizes) {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);
Rect padding = getDefaultPadding();
@@ -318,20 +342,20 @@ public class AppWidgetHostView extends FrameLayout {
float xPaddingDips = (padding.left + padding.right) / density;
float yPaddingDips = (padding.top + padding.bottom) / density;
- ArrayList<PointF> paddedSizes = new ArrayList<>(sizes.size());
+ ArrayList<SizeF> paddedSizes = new ArrayList<>(sizes.size());
float minWidth = Float.MAX_VALUE;
float maxWidth = 0;
float minHeight = Float.MAX_VALUE;
float maxHeight = 0;
for (int i = 0; i < sizes.size(); i++) {
- PointF size = sizes.get(i);
- PointF paddedPoint = new PointF(Math.max(0.f, size.x - xPaddingDips),
- Math.max(0.f, size.y - yPaddingDips));
- paddedSizes.add(paddedPoint);
- minWidth = Math.min(minWidth, paddedPoint.x);
- maxWidth = Math.max(maxWidth, paddedPoint.x);
- minHeight = Math.min(minHeight, paddedPoint.y);
- maxHeight = Math.max(maxHeight, paddedPoint.y);
+ SizeF size = sizes.get(i);
+ SizeF paddedSize = new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
+ Math.max(0.f, size.getHeight() - yPaddingDips));
+ paddedSizes.add(paddedSize);
+ minWidth = Math.min(minWidth, paddedSize.getWidth());
+ maxWidth = Math.max(maxWidth, paddedSize.getWidth());
+ minHeight = Math.min(minHeight, paddedSize.getHeight());
+ maxHeight = Math.max(maxHeight, paddedSize.getHeight());
}
if (paddedSizes.equals(
widgetManager.getAppWidgetOptions(mAppWidgetId).<PointF>getParcelableArrayList(
@@ -348,35 +372,6 @@ public class AppWidgetHostView extends FrameLayout {
}
/**
- * Set the current size of the widget. This should be the full area the AppWidgetHostView is
- * given. Padding added by the framework will be accounted for automatically.
- *
- * This size will be used to choose the appropriate layout the next time the {@link RemoteViews}
- * is re-inflated, if it was created with {@link RemoteViews#RemoteViews(Map)} .
- */
- public void setCurrentSize(@NonNull PointF size) {
- Rect padding = getDefaultPadding();
- float density = getResources().getDisplayMetrics().density;
- float xPaddingDips = (padding.left + padding.right) / density;
- float yPaddingDips = (padding.top + padding.bottom) / density;
- PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
- if (!newSize.equals(mCurrentSize)) {
- mCurrentSize = newSize;
- reapplyLastRemoteViews();
- }
- }
-
- /**
- * Clear the current size, indicating it is not currently known.
- */
- public void clearCurrentSize() {
- if (mCurrentSize != null) {
- mCurrentSize = null;
- reapplyLastRemoteViews();
- }
- }
-
- /**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -514,23 +509,22 @@ public class AppWidgetHostView extends FrameLayout {
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
+ // Select the remote view we are actually going to apply.
+ RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
if (mOnLightBackground) {
- remoteViews = remoteViews.getDarkTextViews();
+ rvToApply = rvToApply.getDarkTextViews();
}
if (mAsyncExecutor != null && useAsyncIfPossible) {
- inflateAsync(remoteViews);
+ inflateAsync(rvToApply);
return;
}
- // Prepare a local reference to the remote Context so we're ready to
- // inflate any requested LayoutParams.
- mRemoteContext = getRemoteContext();
- int layoutId = remoteViews.getLayoutId();
+ int layoutId = rvToApply.getLayoutId();
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
if (content == null && layoutId == mLayoutId) {
try {
- remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
+ rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
content = mView;
recycled = true;
@@ -543,7 +537,7 @@ public class AppWidgetHostView extends FrameLayout {
// Try normal RemoteView inflation
if (content == null) {
try {
- content = remoteViews.apply(mContext, this, mInteractionHandler,
+ content = rvToApply.apply(mContext, this, mInteractionHandler,
mCurrentSize, mColorResources);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 38919f61d9df..a88aed7f20a3 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -219,7 +219,7 @@ public class AppWidgetManager {
public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";
/**
- * A bundle extra ({@code List<PointF>}) that contains the list of possible sizes, in dips, a
+ * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a
* widget instance can take.
*/
public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e7661dbad749..ec94faa544d3 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -374,6 +374,35 @@ public final class BluetoothDevice implements Parcelable {
public static final String ACTION_SDP_RECORD =
"android.bluetooth.device.action.SDP_RECORD";
+ /** @hide */
+ @IntDef(prefix = "METADATA_", value = {
+ METADATA_MANUFACTURER_NAME,
+ METADATA_MODEL_NAME,
+ METADATA_SOFTWARE_VERSION,
+ METADATA_HARDWARE_VERSION,
+ METADATA_COMPANION_APP,
+ METADATA_MAIN_ICON,
+ METADATA_IS_UNTETHERED_HEADSET,
+ METADATA_UNTETHERED_LEFT_ICON,
+ METADATA_UNTETHERED_RIGHT_ICON,
+ METADATA_UNTETHERED_CASE_ICON,
+ METADATA_UNTETHERED_LEFT_BATTERY,
+ METADATA_UNTETHERED_RIGHT_BATTERY,
+ METADATA_UNTETHERED_CASE_BATTERY,
+ METADATA_UNTETHERED_LEFT_CHARGING,
+ METADATA_UNTETHERED_RIGHT_CHARGING,
+ METADATA_UNTETHERED_CASE_CHARGING,
+ METADATA_ENHANCED_SETTINGS_UI_URI,
+ METADATA_DEVICE_TYPE,
+ METADATA_MAIN_BATTERY,
+ METADATA_MAIN_CHARGING,
+ METADATA_MAIN_LOW_BATTERY_THRESHOLD,
+ METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
+ METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
+ METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MetadataKey{}
+
/**
* Maximum length of a metadata entry, this is to avoid exploding Bluetooth
* disk usage
@@ -523,6 +552,89 @@ public final class BluetoothDevice implements Parcelable {
public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
/**
+ * Type of the Bluetooth device, must be within the list of
+ * BluetoothDevice.DEVICE_TYPE_*
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_DEVICE_TYPE = 17;
+
+ /**
+ * Battery level of the Bluetooth device, use when the Bluetooth device
+ * does not support HFP battery indicator.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MAIN_BATTERY = 18;
+
+ /**
+ * Whether the device is charging.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MAIN_CHARGING = 19;
+
+ /**
+ * The battery threshold of the Bluetooth device to show low battery icon.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20;
+
+ /**
+ * The battery threshold of the left headset to show low battery icon.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21;
+
+ /**
+ * The battery threshold of the right headset to show low battery icon.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22;
+
+ /**
+ * The battery threshold of the case to show low battery icon.
+ * Data type should be {@String} as {@link Byte} array.
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23;
+
+ /**
+ * Device type which is used in METADATA_DEVICE_TYPE
+ * Indicates this Bluetooth device is a standard Bluetooth accessory or
+ * not listed in METADATA_DEVICE_TYPE_*.
+ * @hide
+ */
+ @SystemApi
+ public static final String DEVICE_TYPE_DEFAULT = "Default";
+
+ /**
+ * Device type which is used in METADATA_DEVICE_TYPE
+ * Indicates this Bluetooth device is a watch.
+ * @hide
+ */
+ @SystemApi
+ public static final String DEVICE_TYPE_WATCH = "Watch";
+
+ /**
+ * Device type which is used in METADATA_DEVICE_TYPE
+ * Indicates this Bluetooth device is an untethered headset.
+ * @hide
+ */
+ @SystemApi
+ public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset";
+
+ /**
* Broadcast Action: This intent is used to broadcast the {@link UUID}
* wrapped as a {@link android.os.ParcelUuid} of the remote device after it
* has been fetched. This intent is sent only when the UUIDs of the remote
@@ -2316,7 +2428,7 @@ public final class BluetoothDevice implements Parcelable {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean setMetadata(int key, @NonNull byte[] value) {
+ public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
final IBluetooth service = sService;
if (service == null) {
Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
@@ -2344,7 +2456,7 @@ public final class BluetoothDevice implements Parcelable {
@SystemApi
@Nullable
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public byte[] getMetadata(int key) {
+ public byte[] getMetadata(@MetadataKey int key) {
final IBluetooth service = sService;
if (service == null) {
Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
@@ -2357,4 +2469,14 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
}
+
+ /**
+ * Get the maxinum metadata key ID.
+ *
+ * @return the last supported metadata key
+ * @hide
+ */
+ public static @MetadataKey int getMaxMetadataKey() {
+ return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD;
+ }
}
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..b07d6d5b1bc3 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -60,10 +60,16 @@ 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.
+ *
+ * Using it requires declaring uses-permission
+ * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH} in the manifest.
+ *
* @see AssociationRequest.Builder#setDeviceProfile
*/
- public static final String DEVICE_PROFILE_WATCH =
- "android.app.role.COMPANION_DEVICE_WATCH";
+ public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
/** @hide */
@StringDef(value = { DEVICE_PROFILE_WATCH })
@@ -107,6 +113,17 @@ public final class AssociationRequest implements Parcelable {
*/
private @Nullable String mDeviceProfilePrivilegesDescription = null;
+ /**
+ * The time at which his request was created
+ *
+ * @hide
+ */
+ private long mCreationTime;
+
+ private void onConstructed() {
+ mCreationTime = System.currentTimeMillis();
+ }
+
/** @hide */
public void setCallingPackage(@NonNull String pkg) {
mCallingPackage = pkg;
@@ -187,7 +204,7 @@ public final class AssociationRequest implements Parcelable {
markUsed();
return new AssociationRequest(
mSingleDevice, emptyIfNull(mDeviceFilters),
- mDeviceProfile, null, null);
+ mDeviceProfile, null, null, -1L);
}
}
@@ -228,6 +245,8 @@ public final class AssociationRequest implements Parcelable {
* The user-readable description of the device profile's privileges.
*
* Populated by the system.
+ * @param creationTime
+ * The time at which his request was created
* @hide
*/
@DataClass.Generated.Member
@@ -236,7 +255,8 @@ public final class AssociationRequest implements Parcelable {
@NonNull List<DeviceFilter<?>> deviceFilters,
@Nullable @DeviceProfile String deviceProfile,
@Nullable String callingPackage,
- @Nullable String deviceProfilePrivilegesDescription) {
+ @Nullable String deviceProfilePrivilegesDescription,
+ long creationTime) {
this.mSingleDevice = singleDevice;
this.mDeviceFilters = deviceFilters;
com.android.internal.util.AnnotationValidations.validate(
@@ -246,8 +266,9 @@ public final class AssociationRequest implements Parcelable {
DeviceProfile.class, null, mDeviceProfile);
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
+ this.mCreationTime = creationTime;
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
/**
@@ -284,6 +305,16 @@ public final class AssociationRequest implements Parcelable {
return mDeviceProfilePrivilegesDescription;
}
+ /**
+ * The time at which his request was created
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public long getCreationTime() {
+ return mCreationTime;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -295,7 +326,8 @@ public final class AssociationRequest implements Parcelable {
"deviceFilters = " + mDeviceFilters + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
"callingPackage = " + mCallingPackage + ", " +
- "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription +
+ "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " +
+ "creationTime = " + mCreationTime +
" }";
}
@@ -316,7 +348,8 @@ public final class AssociationRequest implements Parcelable {
&& Objects.equals(mDeviceFilters, that.mDeviceFilters)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mCallingPackage, that.mCallingPackage)
- && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription);
+ && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
+ && mCreationTime == that.mCreationTime;
}
@Override
@@ -331,6 +364,7 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
_hash = 31 * _hash + Objects.hashCode(mCallingPackage);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
+ _hash = 31 * _hash + Long.hashCode(mCreationTime);
return _hash;
}
@@ -350,6 +384,7 @@ public final class AssociationRequest implements Parcelable {
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
if (mCallingPackage != null) dest.writeString(mCallingPackage);
if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
+ dest.writeLong(mCreationTime);
}
@Override
@@ -370,6 +405,7 @@ public final class AssociationRequest implements Parcelable {
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
String callingPackage = (flg & 0x8) == 0 ? null : in.readString();
String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString();
+ long creationTime = in.readLong();
this.mSingleDevice = singleDevice;
this.mDeviceFilters = deviceFilters;
@@ -380,8 +416,9 @@ public final class AssociationRequest implements Parcelable {
DeviceProfile.class, null, mDeviceProfile);
this.mCallingPackage = callingPackage;
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
+ this.mCreationTime = creationTime;
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
@DataClass.Generated.Member
@@ -399,10 +436,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1611692924843L,
+ time = 1614976943652L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+ inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 59646f106db5..b441b364cec8 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -16,6 +16,7 @@
package android.companion;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -141,6 +142,10 @@ public final class CompanionDeviceManager {
* <p>Calling this API requires a uses-feature
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
*
+ * <p>When using {@link AssociationRequest#DEVICE_PROFILE_WATCH watch}
+ * {@link AssociationRequest.Builder#setDeviceProfile profile}, caller must also hold
+ * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}</p>
+ *
* @param request specific details about this request
* @param callback will be called once there's at least one device found for user to choose from
* @param handler A handler to control which thread the callback will be delivered on, or null,
@@ -148,6 +153,9 @@ public final class CompanionDeviceManager {
*
* @see AssociationRequest
*/
+ @RequiresPermission(
+ value = Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH,
+ conditional = true)
public void associate(
@NonNull AssociationRequest request,
@NonNull Callback callback,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f3a4e1f79955..48754cccfe0d 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
@@ -6368,6 +6377,19 @@ public abstract class Context {
}
/**
+ * Creates a context with specific properties and behaviors.
+ *
+ * @param contextParams Parameters for how the new context should behave.
+ * @return A context with the specified behaviors.
+ *
+ * @see ContextParams
+ */
+ @NonNull
+ public Context createContext(@NonNull ContextParams contextParams) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Return a new Context object for the current Context but attribute to a different tag.
* In complex apps attribution tagging can be used to distinguish between separate logical
* parts.
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
new file mode 100644
index 000000000000..16128a650c12
--- /dev/null
+++ b/core/java/android/content/ContextParams.java
@@ -0,0 +1,121 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * This class represents rules around how a context being created via
+ * {@link Context#createContext} should behave.
+ *
+ * <p>One of the dimensions to customize is how permissions should behave.
+ * For example, you can specify how permission accesses from a context should
+ * be attributed in the platform's permission tracking system.
+ *
+ * <p>The two main types of attribution are: against an attribution tag which
+ * is an arbitrary string your app specifies for the purposes of tracking permission
+ * accesses from a given portion of your app; against another package and optionally
+ * its attribution tag if you are accessing the data on behalf of another app and
+ * you will be passing that data to this app. Both attributions are not mutually
+ * exclusive.
+ *
+ * <p>For example if you have a feature "foo" in your app which accesses
+ * permissions on behalf of app "foo.bar.baz" with feature "bar" you need to
+ * create a context like this:
+ *
+ * <pre class="prettyprint">
+ * context.createContext(new ContextParams.Builder()
+ * .setAttributionTag("foo")
+ * .setReceiverPackage("foo.bar.baz", "bar")
+ * .build())
+ * </pre>
+ *
+ * @see Context#createContext(ContextParams)
+ */
+public final class ContextParams {
+
+ private ContextParams() {
+ /* hide ctor */
+ }
+
+ /**
+ * @return The attribution tag.
+ */
+ @Nullable
+ public String getAttributionTag() {
+ return null;
+ }
+
+ /**
+ * @return The receiving package.
+ */
+ @Nullable
+ public String getReceiverPackage() {
+ return null;
+ }
+
+ /**
+ * @return The receiving package's attribution tag.
+ */
+ @Nullable
+ public String getReceiverAttributionTag() {
+ return null;
+ }
+
+ /**
+ * Builder for creating a {@link ContextParams}.
+ */
+ public static final class Builder {
+
+ /**
+ * Sets an attribution tag against which to track permission accesses.
+ *
+ * @param attributionTag The attribution tag.
+ * @return This builder.
+ */
+ @NonNull
+ public Builder setAttributionTag(@NonNull String attributionTag) {
+ return this;
+ }
+
+ /**
+ * Sets the package and its optional attribution tag that would be receiving
+ * the permission protected data.
+ *
+ * @param packageName The package name receiving the permission protected data.
+ * @param attributionTag An attribution tag of the receiving package.
+ * @return This builder.
+ */
+ @NonNull
+ public Builder setReceiverPackage(@NonNull String packageName,
+ @Nullable String attributionTag) {
+ return this;
+ }
+
+ /**
+ * Creates a new instance. You need to either specify an attribution tag
+ * or a receiver package or both.
+ *
+ * @return The new instance.
+ */
+ @NonNull
+ public ContextParams build() {
+ return new ContextParams();
+ }
+ }
+}
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 144856b68e7f..d0d406a0c9e6 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -4,4 +4,7 @@ per-file ContextWrapper.java = *
per-file IntentFilter.java = toddke@google.com
per-file IntentFilter.java = patb@google.com
per-file Intent.java = toddke@google.com
-per-file Intent.java = patb@google.com \ No newline at end of file
+per-file Intent.java = patb@google.com
+per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
+per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
+per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
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/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index b34574811bca..c91334a5470f 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -21,32 +21,34 @@ import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
-/**
- * {@hide}
- */
+import com.android.internal.infra.AndroidFuture;
+
+/** {@hide} */
interface IShortcutService {
- boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
- int userId);
+ oneway void setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+ int userId, in AndroidFuture callback);
- boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
- int userId);
+ oneway void addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+ int userId, in AndroidFuture callback);
- void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
+ oneway void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId);
- void removeAllDynamicShortcuts(String packageName, int userId);
+ oneway void removeAllDynamicShortcuts(String packageName, int userId);
- boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
+ oneway void updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId,
+ in AndroidFuture callback);
- boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
- in IntentSender resultIntent, int userId);
+ oneway void requestPinShortcut(String packageName, in ShortcutInfo shortcut,
+ in IntentSender resultIntent, int userId, in AndroidFuture callback);
- Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId);
+ oneway void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut,
+ int userId, in AndroidFuture callback);
- void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
- int disabledMessageResId, int userId);
+ oneway void disableShortcuts(String packageName, in List shortcutIds,
+ CharSequence disabledMessage, int disabledMessageResId, int userId);
- void enableShortcuts(String packageName, in List shortcutIds, int userId);
+ oneway void enableShortcuts(String packageName, in List shortcutIds, int userId);
int getMaxShortcutCountPerActivity(String packageName, int userId);
@@ -56,29 +58,31 @@ interface IShortcutService {
int getIconMaxDimensions(String packageName, int userId);
- void reportShortcutUsed(String packageName, String shortcutId, int userId);
+ oneway void reportShortcutUsed(String packageName, String shortcutId, int userId);
- void resetThrottling(); // system only API for developer opsions
+ oneway void resetThrottling(); // system only API for developer opsions
- void onApplicationActive(String packageName, int userId); // system only API for sysUI
+ oneway void onApplicationActive(String packageName, int userId); // system only API for sysUI
byte[] getBackupPayload(int user);
- void applyRestore(in byte[] payload, int user);
+ oneway void applyRestore(in byte[] payload, int user);
boolean isRequestPinItemSupported(int user, int requestType);
// System API used by framework's ShareSheet (ChooserActivity)
- ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId);
+ oneway void getShareTargets(String packageName, in IntentFilter filter, int userId,
+ in AndroidFuture<ParceledListSlice> callback);
boolean hasShareTargets(String packageName, String packageToCheck, int userId);
- void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId);
+ oneway void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId);
- ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId);
+ oneway void getShortcuts(String packageName, int matchFlags, int userId,
+ in AndroidFuture<ParceledListSlice<ShortcutInfo>> callback);
- void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
+ oneway void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
- void updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate,
- in boolean visible, int userId);
-} \ No newline at end of file
+ oneway void updateShortcutVisibility(String callingPkg, String packageName,
+ in byte[] certificate, in boolean visible, int userId);
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 29dea6bb09db..d79b66c1cf56 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3584,30 +3584,18 @@ public abstract class PackageManager {
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* the requisite kernel support to support incremental delivery aka Incremental FileSystem.
*
- * @see IncrementalManager#isFeatureEnabled
- * @hide
- *
- * @deprecated Use {@link #FEATURE_INCREMENTAL_DELIVERY_VERSION} instead.
- */
- @Deprecated
- @SystemApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_INCREMENTAL_DELIVERY =
- "android.software.incremental_delivery";
-
- /**
- * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* feature not present - IncFs is not present on the device.
* 1 - IncFs v1, core features, no PerUid support. Optional in R.
* 2 - IncFs v2, PerUid support, fs-verity support. Required in S.
*
+ * @see IncrementalManager#isFeatureEnabled
* @see IncrementalManager#getVersion()
* @hide
*/
@SystemApi
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION =
- "android.software.incremental_delivery_version";
+ public static final String FEATURE_INCREMENTAL_DELIVERY =
+ "android.software.incremental_delivery";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
@@ -3656,17 +3644,6 @@ public abstract class PackageManager {
public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
/**
- * Feature for {@link android.view.WindowManager.LayoutParams.backgroundBlurRedius} and
- * {@link android.graphics.drawable.BackgroundBlurDrawable}: the device supports cross-layer
- * blurring.
- *
- * @hide
- */
- @SystemApi
- @SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
* a Keystore implementation that can only enforce limited use key in hardware with max usage
* count equals to 1.
@@ -7040,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/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 7ecb11248d23..7696cbe0b631 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -42,6 +42,7 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
@@ -161,18 +162,20 @@ public abstract class RegisteredServicesCache<V> {
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
+ Handler handler = BackgroundThread.getHandler();
+ mContext.registerReceiverAsUser(
+ mPackageReceiver, UserHandle.ALL, intentFilter, null, handler);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mExternalReceiver, sdFilter);
+ mContext.registerReceiver(mExternalReceiver, sdFilter, null, handler);
// Register for user-related events
IntentFilter userFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiver(mUserRemovedReceiver, userFilter);
+ mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, handler);
}
private void handlePackageEvent(Intent intent, int userId) {
@@ -265,7 +268,7 @@ public abstract class RegisteredServicesCache<V> {
public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) {
if (handler == null) {
- handler = new Handler(mContext.getMainLooper());
+ handler = BackgroundThread.getHandler();
}
synchronized (this) {
mHandler = handler;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index d3bac79aa2b9..f584ff33fa50 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
import android.app.Notification;
import android.app.usage.UsageStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -42,10 +43,12 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* <p><code>ShortcutManager</code> executes operations on an app's set of <i>shortcuts</i>, which
@@ -140,13 +143,16 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- return mService.setDynamicShortcuts(mContext.getPackageName(),
- new ParceledListSlice(shortcutInfoList), injectMyUserId());
+ mService.setDynamicShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future);
}
/**
@@ -158,14 +164,17 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
@NonNull
public List<ShortcutInfo> getDynamicShortcuts() {
+ final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
try {
- return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC,
- injectMyUserId()).getList();
+ mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC, injectMyUserId(),
+ future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future).getList();
}
/**
@@ -177,14 +186,17 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
@NonNull
public List<ShortcutInfo> getManifestShortcuts() {
+ final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
try {
- return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST,
- injectMyUserId()).getList();
+ mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST, injectMyUserId(),
+ future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future).getList();
}
/**
@@ -205,14 +217,16 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
@NonNull
public List<ShortcutInfo> getShortcuts(@ShortcutMatchFlags int matchFlags) {
+ final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
try {
- return mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId())
- .getList();
+ mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future).getList();
}
/**
@@ -228,13 +242,16 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- return mService.addDynamicShortcuts(mContext.getPackageName(),
- new ParceledListSlice(shortcutInfoList), injectMyUserId());
+ mService.addDynamicShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future);
}
/**
@@ -287,14 +304,17 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
@NonNull
public List<ShortcutInfo> getPinnedShortcuts() {
+ final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>();
try {
- return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED,
- injectMyUserId()).getList();
+ mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, injectMyUserId(),
+ future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future).getList();
}
/**
@@ -309,13 +329,16 @@ public class ShortcutManager {
*
* @throws IllegalStateException when the user is locked.
*/
+ @WorkerThread
public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- return mService.updateShortcuts(mContext.getPackageName(),
- new ParceledListSlice(shortcutInfoList), injectMyUserId());
+ mService.updateShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future);
}
/**
@@ -584,14 +607,17 @@ public class ShortcutManager {
* @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
* service, or the device is locked.
*/
+ @WorkerThread
public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
@Nullable IntentSender resultIntent) {
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- return mService.requestPinShortcut(mContext.getPackageName(), shortcut,
- resultIntent, injectMyUserId());
+ mService.requestPinShortcut(mContext.getPackageName(), shortcut,
+ resultIntent, injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future);
}
/**
@@ -611,13 +637,16 @@ public class ShortcutManager {
*
* @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
*/
+ @WorkerThread
public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
+ final AndroidFuture<Intent> future = new AndroidFuture<>();
try {
- return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
- injectMyUserId());
+ mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
+ injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future);
}
/**
@@ -650,16 +679,18 @@ public class ShortcutManager {
* @return List of {@link ShareShortcutInfo}s that match the given IntentFilter.
* @hide
*/
+ @WorkerThread
@NonNull
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS)
public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
+ final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>();
try {
- return mService.getShareTargets(mContext.getPackageName(), filter,
- injectMyUserId()).getList();
+ mService.getShareTargets(mContext.getPackageName(), filter, injectMyUserId(), future);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ future.completeExceptionally(e);
}
+ return getFutureOrThrow(future).getList();
}
/**
@@ -788,4 +819,21 @@ public class ShortcutManager {
throw e.rethrowFromSystemServer();
}
}
+
+ private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
+ try {
+ return future.get();
+ } catch (Throwable e) {
+ if (e instanceof ExecutionException) {
+ e = e.getCause();
+ }
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ }
+ if (e instanceof Error) {
+ throw (Error) e;
+ }
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 2ea24f71371b..6f478accedd7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -155,6 +155,7 @@ public class ParsedActivity extends ParsedMainComponent {
alias.nonLocalizedLabel = target.nonLocalizedLabel;
alias.launchMode = target.launchMode;
alias.lockTaskLaunchMode = target.lockTaskLaunchMode;
+ alias.documentLaunchMode = target.documentLaunchMode;
alias.descriptionRes = target.descriptionRes;
alias.screenOrientation = target.screenOrientation;
alias.taskAffinity = target.taskAffinity;
@@ -179,7 +180,6 @@ public class ParsedActivity extends ParsedMainComponent {
// alias.exported = target.exported;
// alias.permission = target.permission;
// alias.splitName = target.splitName;
-// alias.documentLaunchMode = target.documentLaunchMode;
// alias.persistableMode = target.persistableMode;
// alias.rotationAnimation = target.rotationAnimation;
// alias.colorMode = target.colorMode;
diff --git a/core/java/android/content/pm/permission/OWNERS b/core/java/android/content/pm/permission/OWNERS
index d302b0ae1ea8..cf7e6890876a 100644
--- a/core/java/android/content/pm/permission/OWNERS
+++ b/core/java/android/content/pm/permission/OWNERS
@@ -1,10 +1,8 @@
# Bug component: 137825
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
toddke@android.com
toddke@google.com
patb@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-zhanghai@google.com
-evanseverson@google.com
-ntmyren@google.com
+
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/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 2d381eb85e90..de48ed75746d 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -22,6 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.om.OverlayableInfo;
import android.content.res.loader.AssetsProvider;
import android.content.res.loader.ResourcesProvider;
+import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
@@ -344,7 +345,14 @@ public final class ApkAssets {
@UnsupportedAppUsage
public @NonNull String getAssetPath() {
synchronized (this) {
- return nativeGetAssetPath(mNativePtr);
+ return TextUtils.emptyIfNull(nativeGetAssetPath(mNativePtr));
+ }
+ }
+
+ /** @hide */
+ public @NonNull String getDebugName() {
+ synchronized (this) {
+ return nativeGetDebugName(mNativePtr);
}
}
@@ -422,7 +430,7 @@ public final class ApkAssets {
@Override
public String toString() {
- return "ApkAssets{path=" + getAssetPath() + "}";
+ return "ApkAssets{path=" + getDebugName() + "}";
}
/**
@@ -450,6 +458,7 @@ public final class ApkAssets {
@NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
@PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
private static native @NonNull String nativeGetAssetPath(long ptr);
+ private static native @NonNull String nativeGetDebugName(long ptr);
private static native long nativeGetStringBlock(long ptr);
private static native boolean nativeIsUpToDate(long ptr);
private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
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/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 16ab900dee06..07ebbaff67ea 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -24,13 +24,11 @@ import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.os.Build;
import android.util.Rational;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -641,27 +639,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*/
@NonNull
public Set<String> getPhysicalCameraIds() {
- int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
- if (availableCapabilities == null) {
- throw new AssertionError("android.request.availableCapabilities must be non-null "
- + "in the characteristics");
- }
-
- if (!ArrayUtils.contains(availableCapabilities,
- REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
- return Collections.emptySet();
- }
- byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
-
- String physicalCamIdString = null;
- try {
- physicalCamIdString = new String(physicalCamIds, "UTF-8");
- } catch (java.io.UnsupportedEncodingException e) {
- throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
- }
- String[] physicalCameraIdArray = physicalCamIdString.split("\0");
-
- return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+ return mProperties.getPhysicalCameraIds();
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -2931,6 +2909,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.util.Size>("android.scaler.defaultSecureImageSize", android.util.Size.class);
/**
+ * <p>The available multi-resolution stream configurations that this
+ * physical camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ * <p>This list contains a subset of the parent logical camera's multi-resolution stream
+ * configurations which belong to this physical camera, and it will advertise and will only
+ * advertise the maximum supported resolutions for a particular format.</p>
+ * <p>If this camera device isn't a physical camera device constituting a logical camera,
+ * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the
+ * multi-resolution input/output stream configurations of default mode and max resolution
+ * modes. The sizes will be the maximum resolution of a particular format for default mode
+ * and max resolution mode.</p>
+ * <p>This field will only be advertised if the device is a physical camera of a
+ * logical multi-camera device or an ultra high resolution sensor camera. For a logical
+ * multi-camera, the camera API will derive the logical camera’s multi-resolution stream
+ * configurations from all physical cameras. For an ultra high resolution sensor camera, this
+ * is used directly as the camera’s multi-resolution stream configurations.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS =
+ new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.physicalCameraMultiResolutionStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+ /**
+ * <p>The multi-resolution stream configurations supported by this logical camera
+ * or ultra high resolution sensor camera device.</p>
+ * <p>Multi-resolution streams can be used by a LOGICAL_MULTI_CAMERA or an
+ * ULTRA_HIGH_RESOLUTION_SENSOR camera where the images sent or received can vary in
+ * resolution per frame. This is useful in cases where the camera device's effective full
+ * resolution changes depending on factors such as the current zoom level, lighting
+ * condition, focus distance, or pixel mode.</p>
+ * <ul>
+ * <li>For a logical multi-camera implementing optical zoom, at different zoom level, a
+ * different physical camera may be active, resulting in different full-resolution image
+ * sizes.</li>
+ * <li>For an ultra high resolution camera, depending on whether the camera operates in default
+ * mode, or maximum resolution mode, the output full-size images may be of either binned
+ * resolution or maximum resolution.</li>
+ * </ul>
+ * <p>To use multi-resolution output streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats }.
+ * A {@link android.hardware.camera2.MultiResolutionImageReader } can then be created for a
+ * supported format with the MultiResolutionStreamInfo group queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo }.</p>
+ * <p>If a camera device supports multi-resolution output streams for a particular format, for
+ * each of its mandatory stream combinations, the camera device will support using a
+ * MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional details.</p>
+ * <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }.
+ * A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with
+ * the input MultiResolutionStreamInfo group, queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputInfo }.</p>
+ * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+ * {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution
+ * {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported
+ * multi-resolution output stream formats. Refer to
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for
+ * details about the additional mandatory stream combinations in this case.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP =
+ new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ac6ba0a4ac58..af48b71f9962 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -770,6 +770,8 @@ public abstract class CameraDevice implements AutoCloseable {
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.</p>
*
+ * <p>Clients can access the above mandatory stream combination tables via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
*
* <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
* contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
@@ -777,8 +779,33 @@ public abstract class CameraDevice implements AutoCloseable {
* level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not
* supported.</p>
*
- * <p>Clients can access the above mandatory stream combination tables via
- * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+ * <p>Devices capable of multi-resolution output for a particular format (
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}
+ * returns a non-empty list) support using {@link MultiResolutionImageReader} for MAXIMUM
+ * resolution streams of that format for all mandatory stream combinations. For example,
+ * if a LIMITED camera device supports multi-resolution output streams for both {@code JPEG} and
+ * {@code PRIVATE}, in addition to the stream configurations
+ * in the LIMITED and Legacy table above, the camera device supports the following guaranteed
+ * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link
+ * MultiResolutionImageReader} created based on the variable max resolutions supported):
+ *
+ * <table>
+ * <tr><th colspan="7">LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</th></tr>
+ * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr>
+ * <tr> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>No-viewfinder still image capture.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Still capture plus in-app processing.</td> </tr>
+ * </table><br>
+ * <table>
+ * <tr><th colspan="7">LIMITED-level additional guaranteed configurations with MultiResolutionoutputs</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Two-input in-app processing with still capture.</td> </tr>
+ * </table><br>
+ * The same logic applies to other hardware levels and capabilities.
+ * </p>
*
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
@@ -939,6 +966,32 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
+ * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution
+ * {@code YUV} output or supports multi-resolution {@code PRIVATE} input and multi-resolution
+ * {@code PRIVATE} output, the additional mandatory stream combinations for LIMITED and FULL devices are listed
+ * below ({@code MULTI_RES} in the Max size column refers to a
+ * {@link MultiResolutionImageReader} for output, and a multi-resolution
+ * {@link InputConfiguration} for input):
+ * <table>
+ * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+ * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL in-app processing with still capture.</td> </tr>
+ * </table><br>
+ * <table>
+ * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr>
+ * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
+ * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr>
+ * </table><br>
+ * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level.
+ * </p>
+ *
* <h3>Constrained high-speed recording</h3>
*
* <p>The application can use a
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 3e0e3f62574f..a3c6f2f1eafd 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -31,6 +31,7 @@ import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.display.DisplayManager;
@@ -51,6 +52,7 @@ import android.view.Display;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -372,6 +374,47 @@ public final class CameraManager {
}
/**
+ * Get all physical cameras' multi-resolution stream configuration map
+ *
+ * <p>For a logical multi-camera, query the map between physical camera id and
+ * the physical camera's multi-resolution stream configuration. This map is in turn
+ * combined to form the logical camera's multi-resolution stream configuration map.</p>
+ */
+ private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs(
+ CameraMetadataNative info, ICameraService cameraService)
+ throws CameraAccessException {
+ HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations =
+ new HashMap<String, StreamConfiguration[]>();
+
+ // Query the characteristics of all physical sub-cameras, and combine the multi-resolution
+ // stream configurations. Note that framework derived formats such as HEIC and DEPTH_JPEG
+ // aren't supported as multi-resolution input or output formats.
+ Set<String> physicalCameraIds = info.getPhysicalCameraIds();
+ try {
+ for (String physicalCameraId : physicalCameraIds) {
+ CameraMetadataNative physicalCameraInfo =
+ cameraService.getCameraCharacteristics(physicalCameraId);
+ StreamConfiguration[] configs = physicalCameraInfo.get(
+ CameraCharacteristics.
+ SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
+ if (configs != null) {
+ multiResolutionStreamConfigurations.put(physicalCameraId, configs);
+ }
+ }
+
+ // TODO: If this is an ultra high resolution sensor camera, combine the multi-resolution
+ // stream combination from "info" as well.
+ } catch (RemoteException e) {
+ ServiceSpecificException sse = new ServiceSpecificException(
+ ICameraService.ERROR_DISCONNECTED,
+ "Camera service is currently unavailable");
+ throwAsPublicException(sse);
+ }
+
+ return multiResolutionStreamConfigurations;
+ }
+
+ /**
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
*
@@ -418,12 +461,19 @@ public final class CameraManager {
} catch (NumberFormatException e) {
Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
+
boolean hasConcurrentStreams =
CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
info.setDisplaySize(displaySize);
- characteristics = new CameraCharacteristics(info);
+ Map<String, StreamConfiguration[]> multiResolutionSizeMap =
+ getPhysicalCameraMultiResolutionConfigs(info, cameraService);
+ if (multiResolutionSizeMap.size() > 0) {
+ info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
+ }
+
+ characteristics = new CameraCharacteristics(info);
} catch (ServiceSpecificException e) {
throwAsPublicException(e);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
new file mode 100644
index 000000000000..c592f19bc45c
--- /dev/null
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -0,0 +1,309 @@
+/*
+ * 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.hardware.camera2;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
+import android.media.Image;
+import android.media.ImageReader;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+
+
+import java.nio.NioUtils;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * <p>The MultiResolutionImageReader class wraps a group of {@link ImageReader ImageReaders} with
+ * the same format and different sizes, source camera Id, or camera sensor modes.</p>
+ *
+ * <p>The main use case of this class is for a
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical
+ * multi-camera} or an ultra high resolution sensor camera to output variable-size images. For a
+ * logical multi-camera which implements optical zoom, different physical cameras may have different
+ * maximum resolutions. As a result, when the camera device switches between physical cameras
+ * depending on zoom ratio, the maximum resolution for a particular format may change. For an
+ * ultra high resolution sensor camera, the camera device may deem it better or worse to run in
+ * maximum resolution mode / default mode depending on lighting conditions. So the application may
+ * choose to let the camera device decide on its behalf.</p>
+ *
+ * <p>MultiResolutionImageReader should be used for a camera device only if the camera device
+ * supports multi-resolution output stream by advertising the specified output format in {@link
+ * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}.</p>
+ *
+ * <p>To acquire images from the MultiResolutionImageReader, the application must use the
+ * {@link ImageReader} object passed by
+ * {@link ImageReader.OnImageAvailableListener#onImageAvailable} callback to call
+ * {@link ImageReader#acquireNextImage} or {@link ImageReader#acquireLatestImage}. The application
+ * must not use the {@link ImageReader} passed by an {@link
+ * ImageReader.OnImageAvailableListener#onImageAvailable} callback to acquire future images
+ * because future images may originate from a different {@link ImageReader} contained within the
+ * {@code MultiResolutionImageReader}.</p>
+ *
+ *
+ * @see ImageReader
+ * @see android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ */
+public class MultiResolutionImageReader implements AutoCloseable {
+
+ private static final String TAG = "MultiResolutionImageReader";
+
+ /**
+ * <p>
+ * Create a new multi-resolution reader based on a group of camera stream properties returned
+ * by a camera device.
+ * </p>
+ * <p>
+ * The valid size and formats depend on the camera characteristics.
+ * {@code MultiResolutionImageReader} for an image format is supported by the camera device if
+ * the format is in the supported multi-resolution output stream formats returned by
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+ * If the image format is supported, the {@code MultiResolutionImageReader} object can be
+ * created with the {@code streams} objects returned by
+ * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}.
+ * </p>
+ * <p>
+ * The {@code maxImages} parameter determines the maximum number of
+ * {@link Image} objects that can be be acquired from each of the {@code ImageReader}
+ * within the {@code MultiResolutionImageReader}. However, requesting more buffers will
+ * use up more memory, so it is important to use only the minimum number necessary. The
+ * application is strongly recommended to acquire no more than {@code maxImages} images
+ * from all of the internal ImageReader objects combined. By keeping track of the number of
+ * acquired images for the MultiResolutionImageReader, the application doesn't need to do the
+ * bookkeeping for each internal ImageReader returned from {@link
+ * ImageReader.OnImageAvailableListener#onImageAvailable onImageAvailable} callback.
+ * </p>
+ * <p>
+ * Unlike the normal ImageReader, the MultiResolutionImageReader has a more complex
+ * configuration sequence. Instead of passing the same surface to OutputConfiguration and
+ * CaptureRequest, the
+ * {@link android.hardware.camera2.params.OutputConfiguration#createInstancesForMultiResolutionOutput}
+ * call needs to be used to create the OutputConfigurations for session creation, and then
+ * {@link #getSurface} is used to get {@link CaptureRequest.Builder#addTarget the target for
+ * CaptureRequest}.
+ * </p>
+ * @param streams The group of multi-resolution stream info, which is used to create
+ * a multi-resolution reader containing a number of ImageReader objects. Each
+ * ImageReader object represents a multi-resolution stream in the group.
+ * @param format The format of the Image that this multi-resolution reader will produce.
+ * This must be one of the {@link android.graphics.ImageFormat} or
+ * {@link android.graphics.PixelFormat} constants. Note that not all formats are
+ * supported, like ImageFormat.NV21. The supported multi-resolution
+ * reader format can be queried by {@link
+ * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}.
+ * @param maxImages The maximum number of images the user will want to
+ * access simultaneously. This should be as small as possible to
+ * limit memory use. Once maxImages images are obtained by the
+ * user from any given internal ImageReader, one of them has to be released before
+ * a new Image will become available for access through the ImageReader's
+ * {@link ImageReader#acquireLatestImage()} or
+ * {@link ImageReader#acquireNextImage()}. Must be greater than 0.
+ * @see Image
+ * @see
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ * @see
+ * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap
+ */
+ public static @NonNull MultiResolutionImageReader newInstance(
+ @NonNull Collection<MultiResolutionStreamInfo> streams,
+ @Format int format,
+ @IntRange(from = 1) int maxImages) {
+ return new MultiResolutionImageReader(streams, format, maxImages);
+ }
+
+ /**
+ * @hide
+ */
+ protected MultiResolutionImageReader(Collection<MultiResolutionStreamInfo> streams,
+ int format, int maxImages) {
+ mFormat = format;
+ mMaxImages = maxImages;
+
+ if (streams == null || streams.size() <= 1) {
+ throw new IllegalArgumentException(
+ "The streams info collection must contain at least 2 entries");
+ }
+ if (mMaxImages < 1) {
+ throw new IllegalArgumentException(
+ "Maximum outstanding image count must be at least 1");
+ }
+
+ if (format == ImageFormat.NV21) {
+ throw new IllegalArgumentException(
+ "NV21 format is not supported");
+ }
+
+ int numImageReaders = streams.size();
+ mReaders = new ImageReader[numImageReaders];
+ mStreamInfo = new MultiResolutionStreamInfo[numImageReaders];
+ int index = 0;
+ for (MultiResolutionStreamInfo streamInfo : streams) {
+ mReaders[index] = ImageReader.newInstance(streamInfo.getWidth(),
+ streamInfo.getHeight(), format, maxImages);
+ mStreamInfo[index] = streamInfo;
+ index++;
+ }
+ }
+
+ /**
+ * Set onImageAvailableListener callback.
+ *
+ * <p>This function sets the onImageAvailableListener for all the internal
+ * {@link ImageReader} objects.</p>
+ *
+ * <p>For a multi-resolution ImageReader, the timestamps of images acquired in
+ * onImageAvailable callback from different internal ImageReaders may become
+ * out-of-order due to the asynchronous callbacks between the different resolution
+ * image queues.</p>
+ *
+ * @param listener
+ * The listener that will be run.
+ * @param executor
+ * The executor which will be used when invoking the callback.
+ */
+ @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+ public void setOnImageAvailableListener(
+ @Nullable ImageReader.OnImageAvailableListener listener,
+ @Nullable @CallbackExecutor Executor executor) {
+ for (int i = 0; i < mReaders.length; i++) {
+ mReaders[i].setOnImageAvailableListenerWithExecutor(listener, executor);
+ }
+ }
+
+ @Override
+ public void close() {
+ flush();
+
+ for (int i = 0; i < mReaders.length; i++) {
+ mReaders[i].close();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /**
+ * Flush pending images from all internal ImageReaders
+ *
+ * <p>Acquire and close pending images from all internal ImageReaders. This has the same
+ * effect as calling acquireLatestImage() on all internal ImageReaders, and closing all
+ * latest images.</p>
+ */
+ public void flush() {
+ flushOther(null);
+ }
+
+ /**
+ * Flush pending images from other internal ImageReaders
+ *
+ * <p>Acquire and close pending images from all internal ImageReaders except for the
+ * one specified.</p>
+ *
+ * @param reader The ImageReader object that won't be flushed.
+ *
+ * @hide
+ */
+ public void flushOther(ImageReader reader) {
+ for (int i = 0; i < mReaders.length; i++) {
+ if (reader != null && reader == mReaders[i]) {
+ continue;
+ }
+
+ while (true) {
+ Image image = mReaders[i].acquireNextImageNoThrowISE();
+ if (image == null) {
+ break;
+ } else {
+ image.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the internal ImageReader objects
+ *
+ * @hide
+ */
+ public @NonNull ImageReader[] getReaders() {
+ return mReaders;
+ }
+
+ /**
+ * Get the surface that is used as a target for {@link CaptureRequest}
+ *
+ * <p>The application must use the surface returned by this function as a target for
+ * {@link CaptureRequest}. The camera device makes the decision on which internal
+ * {@code ImageReader} will receive the output image.</p>
+ *
+ * <p>Please note that holding on to the Surface objects returned by this method is not enough
+ * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a
+ * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the
+ * MultiResolutionImageReader that provides it.</p>
+ *
+ * @return a {@link Surface} to use as the target for a capture request.
+ */
+ public @NonNull Surface getSurface() {
+ //TODO: Pick the surface from the reader for default mode stream.
+ return mReaders[0].getSurface();
+ }
+
+ /**
+ * Get the MultiResolutionStreamInfo describing the ImageReader an image originates from
+ *
+ *<p>An image from a {@code MultiResolutionImageReader} is produced from one of the underlying
+ *{@code ImageReader}s. This function returns the {@link MultiResolutionStreamInfo} to describe
+ *the property for that {@code ImageReader}, such as width, height, and physical camera Id.</p>
+ *
+ * @param reader An internal ImageReader within {@code MultiResolutionImageReader}.
+ *
+ * @return The stream info describing the internal {@code ImageReader}.
+ */
+ public @NonNull MultiResolutionStreamInfo getStreamInfoForImageReader(
+ @NonNull ImageReader reader) {
+ for (int i = 0; i < mReaders.length; i++) {
+ if (reader == mReaders[i]) {
+ return mStreamInfo[i];
+ }
+ }
+
+ throw new IllegalArgumentException("ImageReader doesn't belong to this multi-resolution "
+ + "imagereader");
+ }
+
+ // mReaders and mStreamInfo has the same length, and their entries are 1:1 mapped.
+ private final ImageReader[] mReaders;
+ private final MultiResolutionStreamInfo[] mStreamInfo;
+
+ private final int mFormat;
+ private final int mMaxImages;
+}
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index da65f71ce02c..df8eeccbe800 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -61,8 +61,8 @@ public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
private final int mSessionId;
- // The map between physical camera id and capture result
- private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
+ // The map between physical camera ids and their total capture result
+ private final HashMap<String, TotalCaptureResult> mPhysicalCaptureResults;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -83,10 +83,11 @@ public final class TotalCaptureResult extends CaptureResult {
mSessionId = sessionId;
- mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
- CaptureResult physicalResult = new CaptureResult(onePhysicalResult.getCameraId(),
- onePhysicalResult.getCameraMetadata(), parent, extras);
+ TotalCaptureResult physicalResult = new TotalCaptureResult(
+ onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+ parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
physicalResult);
}
@@ -103,7 +104,7 @@ public final class TotalCaptureResult extends CaptureResult {
mPartialResults = new ArrayList<>();
mSessionId = CameraCaptureSession.SESSION_ID_NONE;
- mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+ mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
}
/**
@@ -146,8 +147,37 @@ public final class TotalCaptureResult extends CaptureResult {
* cameras. Otherwise, an empty map is returned.</p>
* @return unmodifiable map between physical camera ids and their capture result metadata
+ *
+ * @deprecated
+ * <p>Please use {@link #getPhysicalCameraTotalResults() instead to get the
+ * physical cameras' {@code TotalCaptureResult}.</p>
*/
public Map<String, CaptureResult> getPhysicalCameraResults() {
return Collections.unmodifiableMap(mPhysicalCaptureResults);
}
+
+ /**
+ * Get the map between physical camera ids and their total capture result metadata
+ *
+ * <p>This function can be called for logical multi-camera devices, which are devices that have
+ * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability.</p>
+ *
+ * <p>If one or more streams from the underlying physical cameras were requested by the
+ * corresponding capture request, this function returns the total result metadata for those
+ * physical cameras. Otherwise, an empty map is returned.</p>
+ *
+ * <p>This function replaces the deprecated {@link #getPhysicalCameraResults}, and its return
+ * value is a map of TotalCaptureResult rather than CaptureResult. </p>
+ *
+ * <p>To reprocess an image from a physical camera stream, typically returned from a
+ * {@link MultiResolutionImageReader}, the application must look up this map to get the {@link
+ * TotalCaptureResult} from the physical camera and pass it to {@link
+ * CameraDevice#createReprocessCaptureRequest}.</p>
+ *
+ * @return unmodifiable map between physical camera ids and their total capture result metadata
+ */
+ @NonNull
+ public Map<String, TotalCaptureResult> getPhysicalCameraTotalResults() {
+ return Collections.unmodifiableMap(mPhysicalCaptureResults);
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ce3c81a2bfd6..4defd23231b8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -36,6 +36,8 @@ import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -468,7 +470,8 @@ public class CameraDeviceImpl extends CameraDevice
}
if (inputConfig != null) {
int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
- inputConfig.getHeight(), inputConfig.getFormat());
+ inputConfig.getHeight(), inputConfig.getFormat(),
+ inputConfig.isMultiResolution());
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
streamId, inputConfig);
}
@@ -1355,7 +1358,42 @@ public class CameraDeviceImpl extends CameraDevice
}
private void checkInputConfiguration(InputConfiguration inputConfig) {
- if (inputConfig != null) {
+ if (inputConfig == null) {
+ return;
+ }
+
+ if (inputConfig.isMultiResolution()) {
+ MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get(
+ CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP);
+
+ int[] inputFormats = configMap.getInputFormats();
+ boolean validFormat = false;
+ for (int format : inputFormats) {
+ if (format == inputConfig.getFormat()) {
+ validFormat = true;
+ }
+ }
+
+ if (validFormat == false) {
+ throw new IllegalArgumentException("multi-resolution input format " +
+ inputConfig.getFormat() + " is not valid");
+ }
+
+ boolean validSize = false;
+ Collection<MultiResolutionStreamInfo> inputStreamInfo =
+ configMap.getInputInfo(inputConfig.getFormat());
+ for (MultiResolutionStreamInfo info : inputStreamInfo) {
+ if (inputConfig.getWidth() == info.getWidth() &&
+ inputConfig.getHeight() == info.getHeight()) {
+ validSize = true;
+ }
+ }
+
+ if (validSize == false) {
+ throw new IllegalArgumentException("Multi-resolution input size " +
+ inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid");
+ }
+ } else {
StreamConfigurationMap configMap = mCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e9bae0b621ce..0cdf744ecd68 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,6 +16,7 @@
package android.hardware.camera2.impl;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.ImageFormat;
import android.graphics.Point;
@@ -53,6 +54,7 @@ import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
import android.hardware.camera2.params.MandatoryStreamCombination;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
import android.hardware.camera2.params.OisSample;
import android.hardware.camera2.params.RecommendedStreamConfiguration;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -61,6 +63,7 @@ import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.TonemapCurve;
+import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.TypeReference;
import android.location.Location;
import android.location.LocationManager;
@@ -79,9 +82,14 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
@@ -747,6 +755,15 @@ public class CameraMetadataNative implements Parcelable {
return (T) metadata.getExtendedSceneModeCapabilities();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMultiResolutionStreamConfigurationMap();
+ }
+ });
}
private int[] getAvailableFormats() {
@@ -1688,6 +1705,7 @@ public class CameraMetadataNative implements Parcelable {
private boolean mHasMandatoryConcurrentStreams = false;
private Size mDisplaySize = new Size(0, 0);
private long mBufferSize = 0;
+ private MultiResolutionStreamConfigurationMap mMultiResolutionStreamConfigurationMap = null;
/**
* Set the current camera Id.
@@ -1723,6 +1741,30 @@ public class CameraMetadataNative implements Parcelable {
mDisplaySize = displaySize;
}
+ /**
+ * Set the multi-resolution stream configuration map.
+ *
+ * @param multiResolutionMap The multi-resolution stream configuration map.
+ *
+ * @hide
+ */
+ public void setMultiResolutionStreamConfigurationMap(
+ @NonNull Map<String, StreamConfiguration[]> multiResolutionMap) {
+ mMultiResolutionStreamConfigurationMap =
+ new MultiResolutionStreamConfigurationMap(multiResolutionMap);
+ }
+
+ /**
+ * Get the multi-resolution stream configuration map.
+ *
+ * @return The multi-resolution stream configuration map.
+ *
+ * @hide
+ */
+ public MultiResolutionStreamConfigurationMap getMultiResolutionStreamConfigurationMap() {
+ return mMultiResolutionStreamConfigurationMap;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
@@ -1777,6 +1819,7 @@ public class CameraMetadataNative implements Parcelable {
mCameraId = other.mCameraId;
mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
mDisplaySize = other.mDisplaySize;
+ mMultiResolutionStreamConfigurationMap = other.mMultiResolutionStreamConfigurationMap;
updateNativeAllocation();
other.updateNativeAllocation();
}
@@ -1980,6 +2023,39 @@ public class CameraMetadataNative implements Parcelable {
return true;
}
+ /**
+ * Return the set of physical camera ids that this logical {@link CameraDevice} is made
+ * up of.
+ *
+ * If the camera device isn't a logical camera, return an empty set.
+ *
+ * @hide
+ */
+ public Set<String> getPhysicalCameraIds() {
+ int[] availableCapabilities = get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ if (availableCapabilities == null) {
+ throw new AssertionError("android.request.availableCapabilities must be non-null "
+ + "in the characteristics");
+ }
+
+ if (!ArrayUtils.contains(availableCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+ return Collections.emptySet();
+ }
+ byte[] physicalCamIds = get(CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+ String physicalCamIdString = null;
+ try {
+ physicalCamIdString = new String(physicalCamIds, "UTF-8");
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+ }
+ String[] physicalCameraIdArray = physicalCamIdString.split("\0");
+
+ return Collections.unmodifiableSet(
+ new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
+ }
+
static {
registerAllMarshalers();
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ba4395f70214..b6b1968bfcdd 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -140,9 +140,10 @@ public class ICameraDeviceUserWrapper {
}
}
- public int createInputStream(int width, int height, int format) throws CameraAccessException {
+ public int createInputStream(int width, int height, int format, boolean isMultiResolution)
+ throws CameraAccessException {
try {
- return mRemoteDevice.createInputStream(width, height, format);
+ return mRemoteDevice.createInputStream(width, height, format, isMultiResolution);
} catch (Throwable t) {
CameraManager.throwAsPublicException(t);
throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index 0a50f974aca8..d63683feed9b 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -16,9 +16,17 @@
package android.hardware.camera2.params;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.ImageFormat.Format;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
+import java.util.Collection;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
/**
* Immutable class to store an input configuration that is used to create a reprocessable capture
* session.
@@ -31,11 +39,12 @@ public final class InputConfiguration {
private final int mWidth;
private final int mHeight;
private final int mFormat;
+ private final boolean mIsMultiResolution;
/**
* Create an input configration with the width, height, and user-defined format.
*
- * <p>Images of an user-defined format are accessible by applications. Use
+ * <p>Images of a user-defined format are accessible by applications. Use
* {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
* to query supported input formats</p>
*
@@ -51,6 +60,52 @@ public final class InputConfiguration {
mWidth = width;
mHeight = height;
mFormat = format;
+ mIsMultiResolution = false;
+ }
+
+ /**
+ * Create an input configration with the format and a list of multi-resolution input stream
+ * info.
+ *
+ * <p>Use {@link
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
+ * to query supported multi-resolution input formats.</p>
+ *
+ * <p>To do reprocessing with variable resolution input, the application calls
+ * {@link android.media.ImageWriter#queueInputImage ImageWriter.queueInputImage}
+ * using an image from an {@link android.media.ImageReader ImageReader} or {@link
+ * android.hardware.camera2.MultiResolutionImageReader MultiResolutionImageReader}. See
+ * {@link android.hardware.camera2.CameraDevice#createReprocessCaptureRequest} for more
+ * details on camera reprocessing.
+ * </p>
+ *
+ * @param multiResolutionInputs A group of multi-resolution input info for the specified format.
+ * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants.
+ *
+ * @see android.graphics.ImageFormat
+ * @see android.graphics.PixelFormat
+ * @see
+ * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
+ */
+ public InputConfiguration(@NonNull Collection<MultiResolutionStreamInfo> multiResolutionInputs,
+ @Format int format) {
+ checkCollectionNotEmpty(multiResolutionInputs, "Input multi-resolution stream info");
+ //TODO: Pick the default mode stream info for ultra-high resolution sensor camera
+ MultiResolutionStreamInfo info = multiResolutionInputs.iterator().next();
+ mWidth = info.getWidth();
+ mHeight = info.getHeight();
+ mFormat = format;
+ mIsMultiResolution = true;
+ }
+
+ /**
+ * @hide
+ */
+ public InputConfiguration(int width, int height, int format, boolean isMultiResolution) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mIsMultiResolution = isMultiResolution;
}
/**
@@ -81,6 +136,18 @@ public final class InputConfiguration {
}
/**
+ * Whether this input configuration is of multi-resolution.
+ *
+ * <p>An multi-resolution InputConfiguration means that the reprocessing session created from it
+ * allows input images of different sizes.</p>
+ *
+ * @return this input configuration is multi-resolution or not.
+ */
+ public boolean isMultiResolution() {
+ return mIsMultiResolution;
+ }
+
+ /**
* Check if this InputConfiguration is equal to another InputConfiguration.
*
* <p>Two input configurations are equal if and only if they have the same widths, heights, and
@@ -100,7 +167,8 @@ public final class InputConfiguration {
if (otherInputConfig.getWidth() == mWidth &&
otherInputConfig.getHeight() == mHeight &&
- otherInputConfig.getFormat() == mFormat) {
+ otherInputConfig.getFormat() == mFormat &&
+ otherInputConfig.isMultiResolution() == mIsMultiResolution) {
return true;
}
return false;
@@ -111,19 +179,21 @@ public final class InputConfiguration {
*/
@Override
public int hashCode() {
- return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat);
+ return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat, mIsMultiResolution ? 1 : 0);
}
/**
* Return this {@link InputConfiguration} as a string representation.
*
- * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d)"}, where {@code %d} represents
- * the width, height, and format, respectively.</p>
+ * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution:%d)"},
+ * where {@code %d} represents the width, height, format, and multi-resolution flag
+ * respectively.</p>
*
* @return string representation of {@link InputConfiguration}
*/
@Override
public String toString() {
- return String.format("InputConfiguration(w:%d, h:%d, format:%d)", mWidth, mHeight, mFormat);
+ return String.format("InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution %b)",
+ mWidth, mHeight, mFormat, mIsMultiResolution);
}
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 776d155e5b3e..8a0172ee8018 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1297,19 +1297,6 @@ public final class MandatoryStreamCombination {
}
/**
- * Size comparison method used by size comparators.
- */
- private static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
- long left = widthA * (long) heightA;
- long right = widthB * (long) heightB;
- if (left == right) {
- left = widthA;
- right = widthB;
- }
- return (left < right) ? -1 : (left > right ? 1 : 0);
- }
-
- /**
* Size comparator that compares the number of pixels it covers.
*
* <p>If two the areas of two sizes are same, compare the widths.</p>
@@ -1317,8 +1304,8 @@ public final class MandatoryStreamCombination {
public static class SizeComparator implements Comparator<Size> {
@Override
public int compare(@NonNull Size lhs, @NonNull Size rhs) {
- return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(),
- rhs.getHeight());
+ return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(),
+ rhs.getWidth(), rhs.getHeight());
}
}
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
new file mode 100644
index 000000000000..1b368fb8c010
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java
@@ -0,0 +1,335 @@
+/*
+ * 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.hardware.camera2.params;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Set;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Immutable class to store the information of the multi-resolution streams supported by
+ * the camera device.
+ *
+ * <p>For a {@link
+ * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+ * logical multi-camera} or an ultra high resolution sensor camera, the maximum resolution of images
+ * produced by the camera device may be variable. For example, for a logical multi-camera, depending
+ * on factors such as current zoom ratio, the camera device may be backed by different physical
+ * cameras. If the physical cameras are of different resolutions, the application may intend to
+ * consume the variable full resolution images from the physical cameras. For an ultra high
+ * resolution sensor camera, the same use case exists where depending on lighting conditions, the
+ * camera device may deem it better to run in default mode and maximum resolution mode.
+ * </p>
+ *
+ * <p>For the use cases described above, multi-resolution output streams can be used by
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to allow the
+ * camera device to output variable size maximum-resolution images.</p>
+ *
+ * <p>Similarly, multi-resolution input streams can be used for reprocessing of variable size
+ * images. In order to reprocess input images of different sizes, the {@link InputConfiguration}
+ * used for creating reprocessable session can be initialized using the group of input stream
+ * configurations returned by {@link #getInputInfo}.</p>
+ */
+public final class MultiResolutionStreamConfigurationMap {
+ /**
+ * Create a new {@link MultiResolutionStreamConfigurationMap}.
+ *
+ * @param configurations a non-{@code null} array of multi-resolution stream
+ * configurations supported by this camera device
+ * @hide
+ */
+ public MultiResolutionStreamConfigurationMap(
+ @NonNull Map<String, StreamConfiguration[]> configurations) {
+ checkNotNull(configurations, "multi-resolution configurations must not be null");
+ if (configurations.size() == 0) {
+ throw new IllegalArgumentException("multi-resolution configurations must not be empty");
+ }
+
+ mConfigurations = configurations;
+
+ // For each multi-resolution stream configuration, track how many formats and sizes there
+ // are available to configure
+ for (Map.Entry<String, StreamConfiguration[]> entry :
+ mConfigurations.entrySet()) {
+ String cameraId = entry.getKey();
+ StreamConfiguration[] configs = entry.getValue();
+
+ for (int i = 0; i < configs.length; i++) {
+ StreamConfiguration config = configs[i];
+ int format = config.getFormat();
+
+ MultiResolutionStreamInfo multiResolutionStreamInfo = new MultiResolutionStreamInfo(
+ config.getWidth(), config.getHeight(), cameraId);
+ Map<Integer, List<MultiResolutionStreamInfo>> destMap;
+ if (config.isInput()) {
+ destMap = mMultiResolutionInputConfigs;
+ } else {
+ destMap = mMultiResolutionOutputConfigs;
+ }
+
+ if (!destMap.containsKey(format)) {
+ List<MultiResolutionStreamInfo> multiResolutionStreamInfoList =
+ new ArrayList<MultiResolutionStreamInfo>();
+ destMap.put(format, multiResolutionStreamInfoList);
+ }
+ destMap.get(format).add(multiResolutionStreamInfo);
+ }
+ }
+ }
+
+ /**
+ * Size comparator that compares the number of pixels two MultiResolutionStreamInfo size covers.
+ *
+ * <p>If two the areas of two sizes are same, compare the widths.</p>
+ *
+ * @hide
+ */
+ public static class SizeComparator implements Comparator<MultiResolutionStreamInfo> {
+ @Override
+ public int compare(@NonNull MultiResolutionStreamInfo lhs,
+ @NonNull MultiResolutionStreamInfo rhs) {
+ return StreamConfigurationMap.compareSizes(
+ lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight());
+ }
+ }
+
+ /**
+ * Get the output formats in this multi-resolution stream configuration.
+ *
+ * <p>A logical multi-camera or an ultra high resolution sensor camera may support
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to dynamically output maximum
+ * resolutions of different sizes (when switching between physical cameras, or between different
+ * modes of an ultra high resolution sensor camera). This function returns the formats
+ * supported for such case.</p>
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * @return an array of integer format, or empty array if multi-resolution output is not
+ * supported
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see android.hardware.camera2.MultiResolutionImageReader
+ */
+ public @NonNull @Format int[] getOutputFormats() {
+ return getPublicImageFormats(/*output*/true);
+ }
+
+ /**
+ * Get the input formats in this multi-resolution stream configuration.
+ *
+ * <p>A logical multi-camera or ultra high resolution sensor camera may support reprocessing
+ * images of different resolutions when switching between physical cameras, or between
+ * different modes of the ultra high resolution sensor camera. This function returns the
+ * formats supported for such case.</p>
+ *
+ * <p>The supported output format for an input format can be queried by calling the camera
+ * device's {@link StreamConfigurationMap#getValidOutputFormatsForInput}.</p>
+ *
+ * <p>All image formats returned by this function will be defined in either {@link ImageFormat}
+ * or in {@link PixelFormat} (and there is no possibility of collision).</p>
+ *
+ * @return an array of integer format, or empty array if no multi-resolution reprocessing is
+ * supported
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ */
+ public @NonNull @Format int[] getInputFormats() {
+ return getPublicImageFormats(/*output*/false);
+ }
+
+ // Get the list of publicly visible multi-resolution input/output stream formats
+ private int[] getPublicImageFormats(boolean output) {
+ Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+ output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+ int formatCount = multiResolutionConfigs.size();
+
+ int[] formats = new int[formatCount];
+ int i = 0;
+ for (Integer format : multiResolutionConfigs.keySet()) {
+ formats[i++] = StreamConfigurationMap.imageFormatToPublic(format);
+ }
+
+ return formats;
+ }
+
+ /**
+ * Get a group of {@code MultiResolutionStreamInfo} with the requested output image
+ * {@code format}
+ *
+ * <p>The {@code format} should be a supported format (one of the formats returned by
+ * {@link #getOutputFormats}).</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+ * a supported multi-resolution output, an empty group is returned.
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see #getOutputFormats
+ */
+ public @NonNull Collection<MultiResolutionStreamInfo> getOutputInfo(@Format int format) {
+ return getInfo(format, /*false*/ true);
+ }
+
+ /**
+ * Get a group of {@code MultiResolutionStreamInfo} with the requested input image {@code format}
+ *
+ * <p>The {@code format} should be a supported format (one of the formats returned by
+ * {@link #getInputFormats}).</p>
+ *
+ * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
+ * @return
+ * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not
+ * a supported multi-resolution input, an empty group is returned.
+ *
+ * @see ImageFormat
+ * @see PixelFormat
+ * @see #getInputFormats
+ */
+ public @NonNull Collection<MultiResolutionStreamInfo> getInputInfo(@Format int format) {
+ return getInfo(format, /*false*/ false);
+ }
+
+ // Get multi-resolution stream info for a particular format
+ private @NonNull Collection<MultiResolutionStreamInfo> getInfo(int format, boolean output) {
+ int internalFormat = StreamConfigurationMap.imageFormatToInternal(format);
+ Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs =
+ output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs;
+ if (multiResolutionConfigs.containsKey(internalFormat)) {
+ return Collections.unmodifiableCollection(multiResolutionConfigs.get(internalFormat));
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private void appendConfigurationsString(StringBuilder sb, boolean output) {
+ sb.append(output ? "Outputs(" : "Inputs(");
+ int[] formats = getPublicImageFormats(output);
+ if (formats != null) {
+ for (int format : formats) {
+ Collection<MultiResolutionStreamInfo> streamInfoList =
+ getInfo(format, output);
+ sb.append("[" + StreamConfigurationMap.formatToString(format) + ":");
+ for (MultiResolutionStreamInfo streamInfo : streamInfoList) {
+ sb.append(String.format("[w:%d, h:%d, id:%s], ",
+ streamInfo.getWidth(), streamInfo.getHeight(),
+ streamInfo.getPhysicalCameraId()));
+ }
+ // Remove the pending ", "
+ if (sb.charAt(sb.length() - 1) == ' ') {
+ sb.delete(sb.length() - 2, sb.length());
+ }
+ sb.append("]");
+ }
+ }
+ sb.append(")");
+ }
+
+ /**
+ * Check if this {@link MultiResolutionStreamConfigurationMap} is equal to another
+ * {@link MultiResolutionStreamConfigurationMap}.
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MultiResolutionStreamConfigurationMap) {
+ final MultiResolutionStreamConfigurationMap other =
+ (MultiResolutionStreamConfigurationMap) obj;
+ if (!mConfigurations.keySet().equals(other.mConfigurations.keySet())) {
+ return false;
+ }
+
+ for (String id : mConfigurations.keySet()) {
+ if (!Arrays.equals(mConfigurations.get(id), other.mConfigurations.get(id))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCodeGeneric(
+ mConfigurations, mMultiResolutionOutputConfigs, mMultiResolutionInputConfigs);
+ }
+
+ /**
+ * Return this {@link MultiResolutionStreamConfigurationMap} as a string representation.
+ *
+ * <p>{@code "MultiResolutionStreamConfigurationMap(Outputs([format1: [w:%d, h:%d, id:%s], ...
+ * ... [w:%d, h:%d, id:%s]), [format2: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s]], ...),
+ * Inputs([format1: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s], ...).</p>
+ *
+ * @return string representation of {@link MultiResolutionStreamConfigurationMap}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("MultiResolutionStreamConfigurationMap(");
+ appendConfigurationsString(sb, /*output*/ true);
+ sb.append(",");
+ appendConfigurationsString(sb, /*output*/ false);
+ sb.append(")");
+
+ return sb.toString();
+ }
+
+
+ private final Map<String, StreamConfiguration[]> mConfigurations;
+
+ /** Format -> list of MultiResolutionStreamInfo used to create MultiResolutionImageReader */
+ private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionOutputConfigs
+ = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+ /** Format -> list of MultiResolutionStreamInfo used for multi-resolution reprocessing */
+ private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionInputConfigs
+ = new HashMap<Integer, List<MultiResolutionStreamInfo>>();
+}
diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java
new file mode 100644
index 000000000000..aa1d1d4aaa18
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.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.hardware.camera2.params;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A utility class describing the properties of one stream of fixed-size image buffers
+ * backing a multi-resolution image stream.
+ *
+ * <p>A group of {@link MultiResolutionStreamInfo} are used to describe the properties of a
+ * multi-resolution image stream for a particular format. The
+ * {@link android.hardware.camera2.MultiResolutionImageReader} class represents a
+ * multi-resolution output stream, and is constructed using a group of
+ * {@link MultiResolutionStreamInfo}. A group of {@link MultiResolutionStreamInfo} can also be used
+ * to create a multi-resolution reprocessable camera capture session. See
+ * {@link android.hardware.camera2.params.InputConfiguration} for details.</p>
+ *
+ * @see InputConfiguration
+ * @see android.hardware.camera2.MultiResolutionImageReader
+ */
+public class MultiResolutionStreamInfo {
+ private int mStreamWidth;
+ private int mStreamHeight;
+ private String mPhysicalCameraId;
+
+ /**
+ * Create a new {@link MultiResolutionStreamInfo}.
+ *
+ * <p>This class creates a {@link MultiResolutionStreamInfo} using image width, image height,
+ * and the physical camera Id images originate from.</p>
+ *
+ * <p>Normally applications do not need to create these directly. Use {@link
+ * MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link
+ * MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format
+ * instead.</p>
+ */
+ public MultiResolutionStreamInfo(int streamWidth, int streamHeight,
+ @NonNull String physicalCameraId) {
+ mStreamWidth = streamWidth;
+ mStreamHeight = streamHeight;
+ mPhysicalCameraId = physicalCameraId;
+ }
+
+ /**
+ * The width of this particular image buffer stream in pixels.
+ */
+ public int getWidth() {
+ return mStreamWidth;
+ }
+
+ /**
+ * The height of this particular image buffer stream in pixels.
+ */
+ public int getHeight() {
+ return mStreamHeight;
+ }
+
+ /**
+ * The physical camera Id of this particular image buffer stream.
+ */
+ public @NonNull String getPhysicalCameraId() {
+ return mPhysicalCameraId;
+ }
+
+ /**
+ * Check if this {@link MultiResolutionStreamInfo} is equal to another
+ * {@link MultiResolutionStreamInfo}.
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MultiResolutionStreamInfo) {
+ final MultiResolutionStreamInfo other = (MultiResolutionStreamInfo) obj;
+ return mStreamWidth == other.mStreamWidth &&
+ mStreamHeight == other.mStreamHeight &&
+ mPhysicalCameraId.equals(other.mPhysicalCameraId);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mStreamWidth, mStreamHeight, mPhysicalCameraId);
+ }
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a20a1bf194ea..e31bd601fc03 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -24,9 +24,13 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.MultiResolutionImageReader;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -34,6 +38,7 @@ import android.util.Size;
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -81,6 +86,13 @@ import java.util.Objects;
* {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
* finalize method returns without exceptions.</li>
*
+ * <li>If the camera device supports multi-resolution output streams, {@link
+ * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} will contain the
+ * formats and their corresponding stream info. The application can use an OutputConfiguration
+ * created with the multi-resolution stream info queried from {@link
+ * MultiResolutionStreamConfigurationMap#getOutputInfo} and
+ * {@link android.hardware.camera2.MultiResolutionImageReader} to capture variable size images.
+ *
* </ul>
*
* <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
@@ -88,6 +100,7 @@ import java.util.Objects;
* device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
+ * @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP
*
*/
public final class OutputConfiguration implements Parcelable {
@@ -206,6 +219,33 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Set the multi-resolution output flag.
+ *
+ * <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group
+ * used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
+ *
+ * <p>This function must only be called for an OutputConfiguration with a non-negative
+ * group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same
+ * group ID and have this flag set.</p>
+ *
+ * @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing}
+ * call, or no non-negative group ID has been set.
+ * @hide
+ */
+ void setMultiResolutionOutput() {
+ if (mIsShared) {
+ throw new IllegalStateException("Multi-resolution output flag must not be set for " +
+ "configuration with surface sharing");
+ }
+ if (mSurfaceGroupId == SURFACE_GROUP_ID_NONE) {
+ throw new IllegalStateException("Multi-resolution output flag should only be set for " +
+ "surface with non-negative group ID");
+ }
+
+ mIsMultiResolution = true;
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance.
*
* <p>This constructor takes an argument for desired camera rotation</p>
@@ -265,6 +305,45 @@ public final class OutputConfiguration implements Parcelable {
mIsDeferredConfig = false;
mIsShared = false;
mPhysicalCameraId = null;
+ mIsMultiResolution = false;
+ }
+
+ /**
+ * Create a list of {@link OutputConfiguration} instances for the outputs used by a
+ * {@link android.hardware.camera2.MultiResolutionImageReader}.
+ *
+ * <p>This constructor takes an argument for a
+ * {@link android.hardware.camera2.MultiResolutionImageReader}.</p>
+ *
+ * @param multiResolutionImageReader
+ * The multi-resolution image reader object.
+ */
+ public static @NonNull Collection<OutputConfiguration> createInstancesForMultiResolutionOutput(
+ @NonNull MultiResolutionImageReader multiResolutionImageReader) {
+ checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
+
+ int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER;
+ MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+ // Skip in case the group id counter overflows to -1, the invalid value.
+ if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
+ MULTI_RESOLUTION_GROUP_ID_COUNTER++;
+ }
+
+ ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
+ ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
+ for (int i = 0; i < imageReaders.length; i++) {
+ MultiResolutionStreamInfo streamInfo =
+ multiResolutionImageReader.getStreamInfoForImageReader(imageReaders[i]);
+
+ OutputConfiguration config = new OutputConfiguration(
+ groupId, imageReaders[i].getSurface());
+ config.setPhysicalCameraId(streamInfo.getPhysicalCameraId());
+ config.setMultiResolutionOutput();
+ configs.add(config);
+ // TODO: Set sensor pixel mode for ultra high resolution sensor camera.
+ }
+
+ return configs;
}
/**
@@ -319,6 +398,7 @@ public final class OutputConfiguration implements Parcelable {
mIsDeferredConfig = true;
mIsShared = false;
mPhysicalCameraId = null;
+ mIsMultiResolution = false;
}
/**
@@ -355,8 +435,18 @@ public final class OutputConfiguration implements Parcelable {
* <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
* The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
* MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
+ *
+ * <p>This function must not be called from OuptutConfigurations created by {@link
+ * #createInstancesForMultiResolutionOutput}.</p>
+ *
+ * @throws IllegalStateException If this OutputConfiguration is created via {@link
+ * #createInstancesForMultiResolutionOutput} to back a MultiResolutionImageReader.
*/
public void enableSurfaceSharing() {
+ if (mIsMultiResolution) {
+ throw new IllegalStateException("Cannot enable surface sharing on "
+ + "multi-resolution output configurations");
+ }
mIsShared = true;
}
@@ -368,8 +458,7 @@ public final class OutputConfiguration implements Parcelable {
* This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
*
* <p>The valid physical camera ids can be queried by {@link
- * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
- * </p>
+ * CameraCharacteristics#getPhysicalCameraIds}.</p>
*
* <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
* stream.</p>
@@ -380,8 +469,16 @@ public final class OutputConfiguration implements Parcelable {
* after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
* CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
*
- * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
- * or output of a reprocessing request. </p>
+ * <p>As of {@link android.os.Build.VERSION_CODES#S Android 12}, an image buffer from a
+ * physical camera stream can be used for reprocessing to logical camera streams and streams
+ * from the same physical camera if the camera device supports multi-resolution input and output
+ * streams. See {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}
+ * for details. The behaviors of reprocessing from a non-physical camera stream to a physical
+ * camera stream, and from a physical camera stream to a physical camera stream of different
+ * physical camera, are device-specific and not guaranteed to be supported.</p>
+ *
+ * <p>On prior API levels, the surface belonging to a physical camera OutputConfiguration must
+ * not be used as input or output of a reprocessing request. </p>
*/
public void setPhysicalCameraId(@Nullable String physicalCameraId) {
mPhysicalCameraId = physicalCameraId;
@@ -527,6 +624,7 @@ public final class OutputConfiguration implements Parcelable {
this.mIsDeferredConfig = other.mIsDeferredConfig;
this.mIsShared = other.mIsShared;
this.mPhysicalCameraId = other.mPhysicalCameraId;
+ this.mIsMultiResolution = other.mIsMultiResolution;
}
/**
@@ -543,6 +641,7 @@ public final class OutputConfiguration implements Parcelable {
ArrayList<Surface> surfaces = new ArrayList<Surface>();
source.readTypedList(surfaces, Surface.CREATOR);
String physicalCameraId = source.readString();
+ boolean isMultiResolutionOutput = source.readInt() == 1;
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
@@ -566,6 +665,7 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredGenerationId = 0;
}
mPhysicalCameraId = physicalCameraId;
+ mIsMultiResolution = isMultiResolutionOutput;
}
/**
@@ -665,6 +765,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeInt(mIsShared ? 1 : 0);
dest.writeTypedList(mSurfaces);
dest.writeString(mPhysicalCameraId);
+ dest.writeInt(mIsMultiResolution ? 1 : 0);
}
/**
@@ -694,7 +795,8 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
- !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId))
+ !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
+ mIsMultiResolution != other.mIsMultiResolution)
return false;
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
@@ -720,17 +822,24 @@ public final class OutputConfiguration implements Parcelable {
return HashCodeHelpers.hashCode(
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
- mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+ mIsMultiResolution ? 1 : 0);
}
return HashCodeHelpers.hashCode(
mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
mConfiguredSize.hashCode(), mConfiguredFormat,
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
- mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
+ mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
+ mIsMultiResolution ? 1 : 0);
}
private static final String TAG = "OutputConfiguration";
+
+ // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
+ // incremented everytime {@link createInstancesForMultiResolutionOutput} is called.
+ private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
+
private ArrayList<Surface> mSurfaces;
private final int mRotation;
private final int mSurfaceGroupId;
@@ -749,4 +858,7 @@ public final class OutputConfiguration implements Parcelable {
private boolean mIsShared;
// The physical camera id that this output configuration is for.
private String mPhysicalCameraId;
+ // Flag indicating if this config is for a multi-resolution output with a
+ // MultiResolutionImageReader
+ private boolean mIsMultiResolution;
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 8fc919f142a2..ea6b92d4f33e 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -130,11 +130,13 @@ public final class SessionConfiguration implements Parcelable {
int inputWidth = source.readInt();
int inputHeight = source.readInt();
int inputFormat = source.readInt();
+ boolean isInputMultiResolution = source.readBoolean();
ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>();
source.readTypedList(outConfigs, OutputConfiguration.CREATOR);
if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) {
- mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat);
+ mInputConfig = new InputConfiguration(inputWidth, inputHeight,
+ inputFormat, isInputMultiResolution);
}
mSessionType = sessionType;
mOutputConfigurations = outConfigs;
@@ -169,10 +171,12 @@ public final class SessionConfiguration implements Parcelable {
dest.writeInt(mInputConfig.getWidth());
dest.writeInt(mInputConfig.getHeight());
dest.writeInt(mInputConfig.getFormat());
+ dest.writeBoolean(mInputConfig.isMultiResolution());
} else {
dest.writeInt(/*inputWidth*/ 0);
dest.writeInt(/*inputHeight*/ 0);
dest.writeInt(/*inputFormat*/ -1);
+ dest.writeBoolean(/*isMultiResolution*/ false);
}
dest.writeTypedList(mOutputConfigurations);
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 10a814acd70b..a25ae6041d77 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1575,7 +1575,7 @@ public final class StreamConfigurationMap {
return sizes;
}
- /** Get the list of publically visible output formats; does not include IMPL_DEFINED */
+ /** Get the list of publicly visible output formats */
private int[] getPublicFormats(boolean output) {
int[] formats = new int[getPublicFormatCount(output)];
@@ -1746,6 +1746,21 @@ public final class StreamConfigurationMap {
return sb.toString();
}
+ /**
+ * Size comparison method used by size comparators.
+ *
+ * @hide
+ */
+ public static int compareSizes(int widthA, int heightA, int widthB, int heightB) {
+ long left = widthA * (long) heightA;
+ long right = widthB * (long) heightB;
+ if (left == right) {
+ left = widthA;
+ right = widthB;
+ }
+ return (left < right) ? -1 : (left > right ? 1 : 0);
+ }
+
private void appendOutputsString(StringBuilder sb) {
sb.append("Outputs(");
int[] formats = getOutputFormats();
@@ -1843,7 +1858,10 @@ public final class StreamConfigurationMap {
sb.append(")");
}
- private String formatToString(int format) {
+ /**
+ * @hide
+ */
+ public static String formatToString(int format) {
switch (format) {
case ImageFormat.YV12:
return "YV12";
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index e247df320115..aafa7d520632 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -537,6 +537,26 @@ public final class ColorDisplayManager {
}
/**
+ * Returns the minimum allowed brightness reduction strength in percentage when activated.
+ *
+ * @hide
+ */
+ public static int getMinimumReduceBrightColorsStrength(Context context) {
+ return context.getResources()
+ .getInteger(R.integer.config_reduceBrightColorsStrengthMin);
+ }
+
+ /**
+ * Returns the maximum allowed brightness reduction strength in percentage when activated.
+ *
+ * @hide
+ */
+ public static int getMaximumReduceBrightColorsStrength(Context context) {
+ return context.getResources()
+ .getInteger(R.integer.config_reduceBrightColorsStrengthMax);
+ }
+
+ /**
* Check if the color transforms are color accelerated. Some transforms are experimental only
* on non-accelerated platforms due to the performance implications.
*
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 41126b70c89f..9457d8f1aac4 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -16,40 +16,69 @@
package android.hardware.display;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Product-specific information about the display or the directly connected device on the
* display chain. For example, if the display is transitively connected, this field may contain
* product information about the intermediate device.
- * @hide
*/
public final class DeviceProductInfo implements Parcelable {
+ /** @hide */
+ @IntDef(prefix = {"CONNECTION_TO_SINK_"}, value = {
+ CONNECTION_TO_SINK_UNKNOWN,
+ CONNECTION_TO_SINK_BUILT_IN,
+ CONNECTION_TO_SINK_DIRECT,
+ CONNECTION_TO_SINK_TRANSITIVE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConnectionToSinkType { }
+
+ /** The device connection to the display sink is unknown. */
+ public static final int CONNECTION_TO_SINK_UNKNOWN =
+ IDeviceProductInfoConstants.CONNECTION_TO_SINK_UNKNOWN;
+
+ /** The display sink is built-in to the device */
+ public static final int CONNECTION_TO_SINK_BUILT_IN =
+ IDeviceProductInfoConstants.CONNECTION_TO_SINK_BUILT_IN;
+
+ /** The device is directly connected to the display sink. */
+ public static final int CONNECTION_TO_SINK_DIRECT =
+ IDeviceProductInfoConstants.CONNECTION_TO_SINK_DIRECT;
+
+ /** The device is transitively connected to the display sink. */
+ public static final int CONNECTION_TO_SINK_TRANSITIVE =
+ IDeviceProductInfoConstants.CONNECTION_TO_SINK_TRANSITIVE;
+
private final String mName;
private final String mManufacturerPnpId;
private final String mProductId;
private final Integer mModelYear;
private final ManufactureDate mManufactureDate;
- private final int[] mRelativeAddress;
+ private final @ConnectionToSinkType int mConnectionToSinkType;
+ /** @hide */
public DeviceProductInfo(
String name,
String manufacturerPnpId,
String productId,
Integer modelYear,
ManufactureDate manufactureDate,
- int[] relativeAddress) {
+ int connectionToSinkType) {
this.mName = name;
this.mManufacturerPnpId = manufacturerPnpId;
this.mProductId = productId;
this.mModelYear = modelYear;
this.mManufactureDate = manufactureDate;
- this.mRelativeAddress = relativeAddress;
+ this.mConnectionToSinkType = connectionToSinkType;
}
private DeviceProductInfo(Parcel in) {
@@ -58,12 +87,13 @@ public final class DeviceProductInfo implements Parcelable {
mProductId = (String) in.readValue(null);
mModelYear = (Integer) in.readValue(null);
mManufactureDate = (ManufactureDate) in.readValue(null);
- mRelativeAddress = in.createIntArray();
+ mConnectionToSinkType = in.readInt();
}
/**
* @return Display name.
*/
+ @Nullable
public String getName() {
return mName;
}
@@ -71,6 +101,7 @@ public final class DeviceProductInfo implements Parcelable {
/**
* @return Manufacturer Plug and Play ID.
*/
+ @NonNull
public String getManufacturerPnpId() {
return mManufacturerPnpId;
}
@@ -78,32 +109,58 @@ public final class DeviceProductInfo implements Parcelable {
/**
* @return Manufacturer product ID.
*/
+ @NonNull
public String getProductId() {
return mProductId;
}
/**
- * @return Model year of the device. Typically exactly one of model year or
- * manufacture date will be present.
+ * @return Model year of the device. Return -1 if not available. Typically,
+ * one of model year or manufacture year is available.
*/
- public Integer getModelYear() {
- return mModelYear;
+ public int getModelYear() {
+ return mModelYear != null ? mModelYear : -1;
+ }
+
+ /**
+ * @return The year of manufacture, or -1 it is not available. Typically,
+ * one of model year or manufacture year is available.
+ */
+ public int getManufactureYear() {
+ if (mManufactureDate == null) {
+ return -1;
+ }
+ return mManufactureDate.mYear != null ? mManufactureDate.mYear : -1;
+ }
+
+ /**
+ * @return The week of manufacture, or -1 it is not available. Typically,
+ * not present if model year is available.
+ */
+ public int getManufactureWeek() {
+ if (mManufactureDate == null) {
+ return -1;
+ }
+ return mManufactureDate.mWeek != null ? mManufactureDate.mWeek : -1;
}
/**
* @return Manufacture date. Typically exactly one of model year or manufacture
* date will be present.
+ *
+ * @hide
*/
public ManufactureDate getManufactureDate() {
return mManufactureDate;
}
/**
- * @return Relative address in the display network. For example, for HDMI connected devices this
- * can be its physical address. Each component of the address is in the range [0, 255].
+ * @return How the current device is connected to the display sink. For example, the display
+ * can be connected immediately to the device or there can be a receiver in between.
*/
- public int[] getRelativeAddress() {
- return mRelativeAddress;
+ @ConnectionToSinkType
+ public int getConnectionToSinkType() {
+ return mConnectionToSinkType;
}
@Override
@@ -119,8 +176,8 @@ public final class DeviceProductInfo implements Parcelable {
+ mModelYear
+ ", manufactureDate="
+ mManufactureDate
- + ", relativeAddress="
- + Arrays.toString(mRelativeAddress)
+ + ", connectionToSinkType="
+ + mConnectionToSinkType
+ '}';
}
@@ -134,16 +191,16 @@ public final class DeviceProductInfo implements Parcelable {
&& Objects.equals(mProductId, that.mProductId)
&& Objects.equals(mModelYear, that.mModelYear)
&& Objects.equals(mManufactureDate, that.mManufactureDate)
- && Arrays.equals(mRelativeAddress, that.mRelativeAddress);
+ && mConnectionToSinkType == that.mConnectionToSinkType;
}
@Override
public int hashCode() {
return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate,
- Arrays.hashCode(mRelativeAddress));
+ mConnectionToSinkType);
}
- public static final Creator<DeviceProductInfo> CREATOR =
+ @NonNull public static final Creator<DeviceProductInfo> CREATOR =
new Creator<DeviceProductInfo>() {
@Override
public DeviceProductInfo createFromParcel(Parcel in) {
@@ -162,13 +219,13 @@ public final class DeviceProductInfo implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mName);
dest.writeString(mManufacturerPnpId);
dest.writeValue(mProductId);
dest.writeValue(mModelYear);
dest.writeValue(mManufactureDate);
- dest.writeIntArray(mRelativeAddress);
+ dest.writeInt(mConnectionToSinkType);
}
/**
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index 2626a461aaf5..6fb0eb5df59a 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -17,6 +17,7 @@
package android.hardware.lights;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -94,6 +95,20 @@ public final class LightsRequest {
}
/**
+ * Overrides the color and intensity of a given light.
+ *
+ * @param light the light to modify
+ * @param state the desired color and intensity of the light *
+ * @deprecated Use {@link #addLight(Light, LightState)} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+ return addLight(light, state);
+ }
+
+ /**
* Removes the override for the color and intensity of a given light.
*
* @param light the light to modify
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/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index b016ed67c4d9..9bf791ba33e0 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -19,8 +19,6 @@ package android.net;
import android.net.INetworkPolicyListener;
import android.net.Network;
import android.net.NetworkPolicy;
-import android.net.NetworkQuotaInfo;
-import android.net.NetworkState;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionPlan;
@@ -70,9 +68,6 @@ interface INetworkPolicyManager {
int getMultipathPreference(in Network network);
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
-
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 0baf11e850c7..dc3b88a7c3be 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -19,7 +19,7 @@ package android.net;
import android.net.DataUsageRequest;
import android.net.INetworkStatsSession;
import android.net.Network;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
@@ -68,7 +68,7 @@ interface INetworkStatsService {
/** Force update of ifaces. */
void forceUpdateIfaces(
in Network[] defaultNetworks,
- in NetworkState[] networkStates,
+ in NetworkStateSnapshot[] snapshots,
in String activeIface,
in UnderlyingNetworkInfo[] underlyingNetworkInfos);
/** Force update of statistics. */
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 e89451e4f4ef..8f1e2defd215 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -146,6 +146,25 @@ public final class IpSecAlgorithm implements Parcelable {
public static final String AUTH_AES_XCBC = "xcbc(aes)";
/**
+ * AES-CMAC Authentication/Integrity Algorithm.
+ *
+ * <p>Keys for this algorithm must be 128 bits in length.
+ *
+ * <p>The only valid truncation length is 96 bits.
+ *
+ * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+ * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+ * included in the returned algorithm set. The returned algorithm set will not change unless the
+ * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+ * requested on an unsupported device.
+ *
+ * <p>@see {@link #getSupportedAlgorithms()}
+ */
+ // This algorithm may be available on devices released before Android 12, and is guaranteed
+ // to be available on devices first shipped with Android 12 or later.
+ public static final String AUTH_AES_CMAC = "cmac(aes)";
+
+ /**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
* <p>Valid lengths for keying material are {160, 224, 288}.
@@ -191,6 +210,7 @@ public final class IpSecAlgorithm implements Parcelable {
AUTH_HMAC_SHA384,
AUTH_HMAC_SHA512,
AUTH_AES_XCBC,
+ AUTH_AES_CMAC,
AUTH_CRYPT_AES_GCM,
AUTH_CRYPT_CHACHA20_POLY1305
})
@@ -215,6 +235,7 @@ public final class IpSecAlgorithm implements Parcelable {
// 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);
}
@@ -383,6 +404,10 @@ public final class IpSecAlgorithm implements Parcelable {
isValidLen = keyLen == 128;
isValidTruncLen = truncLen == 96;
break;
+ case AUTH_AES_CMAC:
+ isValidLen = keyLen == 128;
+ isValidTruncLen = truncLen == 96;
+ break;
case AUTH_CRYPT_AES_GCM:
// The keying material for GCM is a key plus a 32-bit salt
isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
@@ -416,6 +441,7 @@ public final class IpSecAlgorithm implements Parcelable {
case AUTH_HMAC_SHA384:
case AUTH_HMAC_SHA512:
case AUTH_AES_XCBC:
+ case AUTH_AES_CMAC:
return true;
default:
return false;
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 303a40755d4e..a5ece7b713c7 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -18,7 +18,6 @@ package android.net;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
@@ -180,29 +179,42 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
/**
- * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
- * assuming that any mobile networks are using the current IMSI. The subType if applicable,
- * should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
- * {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
+ * Build a {@link NetworkIdentity} from the given {@link NetworkState} and
+ * {@code subType}, assuming that any mobile networks are using the current IMSI.
+ * The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_*
+ * constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
*/
- public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
- boolean defaultNetwork, @NetworkType int subType) {
- final int legacyType = state.legacyNetworkType;
+ // TODO: Delete this function after NetworkPolicyManagerService finishes the migration.
+ public static NetworkIdentity buildNetworkIdentity(Context context,
+ NetworkState state, boolean defaultNetwork, @NetworkType int subType) {
+ final NetworkStateSnapshot snapshot = new NetworkStateSnapshot(state.network,
+ state.networkCapabilities, state.linkProperties, state.subscriberId,
+ state.legacyNetworkType);
+ return buildNetworkIdentity(context, snapshot, defaultNetwork, subType);
+ }
- String subscriberId = null;
+ /**
+ * Build a {@link NetworkIdentity} from the given {@link NetworkStateSnapshot} and
+ * {@code subType}, assuming that any mobile networks are using the current IMSI.
+ * The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_*
+ * constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
+ */
+ public static NetworkIdentity buildNetworkIdentity(Context context,
+ NetworkStateSnapshot snapshot, boolean defaultNetwork, @NetworkType int subType) {
+ final int legacyType = snapshot.legacyType;
+
+ final String subscriberId = snapshot.subscriberId;
String networkId = null;
- boolean roaming = !state.networkCapabilities.hasCapability(
+ boolean roaming = !snapshot.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- boolean metered = !state.networkCapabilities.hasCapability(
+ boolean metered = !snapshot.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- subscriberId = state.subscriberId;
-
- final int oemManaged = getOemBitfield(state.networkCapabilities);
+ final int oemManaged = getOemBitfield(snapshot.networkCapabilities);
if (legacyType == TYPE_WIFI) {
- if (state.networkCapabilities.getSsid() != null) {
- networkId = state.networkCapabilities.getSsid();
+ if (snapshot.networkCapabilities.getSsid() != null) {
+ networkId = snapshot.networkCapabilities.getSsid();
if (networkId == null) {
// TODO: Figure out if this code path never runs. If so, remove them.
final WifiManager wifi = (WifiManager) context.getSystemService(
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java
index 881b373fa241..0d26c2de8698 100644
--- a/core/java/android/net/NetworkStateSnapshot.java
+++ b/core/java/android/net/NetworkStateSnapshot.java
@@ -16,11 +16,16 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.net.module.util.NetworkIdentityUtils;
+
import java.util.Objects;
/**
@@ -28,31 +33,49 @@ import java.util.Objects;
*
* @hide
*/
+@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkStateSnapshot implements Parcelable {
+ /** The network associated with this snapshot. */
@NonNull
- public final LinkProperties linkProperties;
+ public final Network network;
+
+ /** The {@link NetworkCapabilities} of the network associated with this snapshot. */
@NonNull
public final NetworkCapabilities networkCapabilities;
+
+ /** The {@link LinkProperties} of the network associated with this snapshot. */
@NonNull
- public final Network network;
+ public final LinkProperties linkProperties;
+
+ /**
+ * The Subscriber Id of the network associated with this snapshot. See
+ * {@link android.telephony.TelephonyManager#getSubscriberId()}.
+ */
@Nullable
public final String subscriberId;
+
+ /**
+ * The legacy type of the network associated with this snapshot. See
+ * {@code ConnectivityManager#TYPE_*}.
+ */
public final int legacyType;
- public NetworkStateSnapshot(@NonNull LinkProperties linkProperties,
- @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ public NetworkStateSnapshot(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties,
@Nullable String subscriberId, int legacyType) {
- this.linkProperties = Objects.requireNonNull(linkProperties);
- this.networkCapabilities = Objects.requireNonNull(networkCapabilities);
this.network = Objects.requireNonNull(network);
+ this.networkCapabilities = Objects.requireNonNull(networkCapabilities);
+ this.linkProperties = Objects.requireNonNull(linkProperties);
this.subscriberId = subscriberId;
this.legacyType = legacyType;
}
+ /** @hide */
public NetworkStateSnapshot(@NonNull Parcel in) {
- linkProperties = in.readParcelable(null);
- networkCapabilities = in.readParcelable(null);
network = in.readParcelable(null);
+ networkCapabilities = in.readParcelable(null);
+ linkProperties = in.readParcelable(null);
subscriberId = in.readString();
legacyType = in.readInt();
}
@@ -64,9 +87,9 @@ public final class NetworkStateSnapshot implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeParcelable(linkProperties, flags);
- out.writeParcelable(networkCapabilities, flags);
out.writeParcelable(network, flags);
+ out.writeParcelable(networkCapabilities, flags);
+ out.writeParcelable(linkProperties, flags);
out.writeString(subscriberId);
out.writeInt(legacyType);
}
@@ -93,14 +116,25 @@ public final class NetworkStateSnapshot implements Parcelable {
if (!(o instanceof NetworkStateSnapshot)) return false;
NetworkStateSnapshot that = (NetworkStateSnapshot) o;
return legacyType == that.legacyType
- && Objects.equals(linkProperties, that.linkProperties)
- && Objects.equals(networkCapabilities, that.networkCapabilities)
&& Objects.equals(network, that.network)
+ && Objects.equals(networkCapabilities, that.networkCapabilities)
+ && Objects.equals(linkProperties, that.linkProperties)
&& Objects.equals(subscriberId, that.subscriberId);
}
@Override
public int hashCode() {
- return Objects.hash(linkProperties, networkCapabilities, network, subscriberId, legacyType);
+ return Objects.hash(network, networkCapabilities, linkProperties, subscriberId, legacyType);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkStateSnapshot{"
+ + "network=" + network
+ + ", networkCapabilities=" + networkCapabilities
+ + ", linkProperties=" + linkProperties
+ + ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + '\''
+ + ", legacyType=" + legacyType
+ + '}';
}
}
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/packages/Connectivity/framework/src/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java
index 326943a27d4e..326943a27d4e 100644
--- a/packages/Connectivity/framework/src/android/net/PacProxySelector.java
+++ b/core/java/android/net/PacProxySelector.java
diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 77c8a4f4579b..77c8a4f4579b 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index f90fbaf1e0fb..fa3ff8a26862 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.net.VpnConfig;
import java.net.DatagramSocket;
@@ -254,7 +255,7 @@ public class VpnService extends Service {
* @return {@code true} on success.
*/
public boolean protect(int socket) {
- return NetworkUtils.protectFromVpn(socket);
+ return NetworkUtilsInternal.protectFromVpn(socket);
}
/**
diff --git a/packages/Connectivity/framework/src/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
index e64060f1b220..69edc757ce8a 100644
--- a/packages/Connectivity/framework/src/android/net/util/SocketUtils.java
+++ b/core/java/android/net/util/SocketUtils.java
@@ -22,12 +22,13 @@ import static android.system.OsConstants.SO_BINDTODEVICE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.net.NetworkUtils;
import android.system.ErrnoException;
import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.PacketSocketAddress;
+import com.android.internal.net.NetworkUtilsInternal;
+
import libcore.io.IoBridge;
import java.io.FileDescriptor;
@@ -51,7 +52,7 @@ public final class SocketUtils {
// of struct ifreq is a NULL-terminated interface name.
// TODO: add a setsockoptString()
Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface);
- NetworkUtils.protectFromVpn(socket);
+ NetworkUtilsInternal.protectFromVpn(socket);
}
/**
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/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
index f8ae492016f0..62de8216ce54 100644
--- a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
+++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
@@ -17,6 +17,6 @@
package android.net.vcn;
/** @hide */
-interface IVcnUnderlyingNetworkPolicyListener {
+oneway interface IVcnUnderlyingNetworkPolicyListener {
void onPolicyChanged();
} \ No newline at end of file
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/IkeTrafficSelectorUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java
new file mode 100644
index 000000000000..6bbc6b1e8218
--- /dev/null
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.IkeTrafficSelector;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Provides utility methods to convert IkeTrafficSelector to/from PersistableBundle.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PRIVATE)
+public final class IkeTrafficSelectorUtils {
+ private static final String START_PORT_KEY = "START_PORT_KEY";
+ private static final String END_PORT_KEY = "END_PORT_KEY";
+ private static final String START_ADDRESS_KEY = "START_ADDRESS_KEY";
+ private static final String END_ADDRESS_KEY = "END_ADDRESS_KEY";
+
+ /** Constructs an IkeTrafficSelector by deserializing a PersistableBundle. */
+ @NonNull
+ public static IkeTrafficSelector fromPersistableBundle(@NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle was null");
+
+ final int startPort = in.getInt(START_PORT_KEY);
+ final int endPort = in.getInt(END_PORT_KEY);
+
+ final String startingAddress = in.getString(START_ADDRESS_KEY);
+ final String endingAddress = in.getString(END_ADDRESS_KEY);
+ Objects.requireNonNull(startingAddress, "startAddress was null");
+ Objects.requireNonNull(startingAddress, "endAddress was null");
+
+ return new IkeTrafficSelector(
+ startPort,
+ endPort,
+ InetAddresses.parseNumericAddress(startingAddress),
+ InetAddresses.parseNumericAddress(endingAddress));
+ }
+
+ /** Serializes an IkeTrafficSelector to a PersistableBundle. */
+ @NonNull
+ public static PersistableBundle toPersistableBundle(@NonNull IkeTrafficSelector ts) {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(START_PORT_KEY, ts.startPort);
+ result.putInt(END_PORT_KEY, ts.endPort);
+ result.putString(START_ADDRESS_KEY, ts.startingAddress.getHostAddress());
+ result.putString(END_ADDRESS_KEY, ts.endingAddress.getHostAddress());
+
+ return result;
+ }
+}
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/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java
index 81c781bff06d..a999e658ce21 100644
--- a/core/java/android/os/BatterySaverPolicyConfig.java
+++ b/core/java/android/os/BatterySaverPolicyConfig.java
@@ -247,6 +247,7 @@ public final class BatterySaverPolicyConfig implements Parcelable {
/**
* Get the SoundTrigger mode while in Battery Saver.
*/
+ @PowerManager.SoundTriggerPowerSaveMode
public int getSoundTriggerMode() {
return mSoundTriggerMode;
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index de7b88575e98..f12eb8853c55 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -37,6 +37,8 @@ public final class BatteryUsageStats implements Parcelable {
private final long mStatsStartRealtimeMs;
private final double mDischargedPowerLowerBound;
private final double mDischargedPowerUpperBound;
+ private final long mBatteryTimeRemainingMs;
+ private final long mChargeTimeRemainingMs;
private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers;
private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers;
@@ -50,6 +52,8 @@ public final class BatteryUsageStats implements Parcelable {
mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah;
mHistoryBuffer = builder.mHistoryBuffer;
mHistoryTagPool = builder.mHistoryTagPool;
+ mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs;
+ mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
double totalPower = 0;
@@ -110,6 +114,25 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Returns an approximation for how much run time (in milliseconds) is remaining on
+ * the battery. Returns -1 if no time can be computed: either there is not
+ * enough current data to make a decision, or the battery is currently
+ * charging.
+ */
+ public long getBatteryTimeRemainingMs() {
+ return mBatteryTimeRemainingMs;
+ }
+
+ /**
+ * Returns an approximation for how much time (in milliseconds) remains until the battery
+ * is fully charged. Returns -1 if no time can be computed: either there is not
+ * enough current data to make a decision, or the battery is currently discharging.
+ */
+ public long getChargeTimeRemainingMs() {
+ return mChargeTimeRemainingMs;
+ }
+
+ /**
* Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), in mAh
*/
@@ -156,6 +179,8 @@ public final class BatteryUsageStats implements Parcelable {
mDischargePercentage = source.readInt();
mDischargedPowerLowerBound = source.readDouble();
mDischargedPowerUpperBound = source.readDouble();
+ mBatteryTimeRemainingMs = source.readLong();
+ mChargeTimeRemainingMs = source.readLong();
mUidBatteryConsumers = new ArrayList<>();
source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
mSystemBatteryConsumers = new ArrayList<>();
@@ -194,6 +219,8 @@ public final class BatteryUsageStats implements Parcelable {
dest.writeInt(mDischargePercentage);
dest.writeDouble(mDischargedPowerLowerBound);
dest.writeDouble(mDischargedPowerUpperBound);
+ dest.writeLong(mBatteryTimeRemainingMs);
+ dest.writeLong(mChargeTimeRemainingMs);
dest.writeParcelableList(mUidBatteryConsumers, flags);
dest.writeParcelableList(mSystemBatteryConsumers, flags);
dest.writeParcelableList(mUserBatteryConsumers, flags);
@@ -237,6 +264,8 @@ public final class BatteryUsageStats implements Parcelable {
private int mDischargePercentage;
private double mDischargedPowerLowerBoundMah;
private double mDischargedPowerUpperBoundMah;
+ private long mBatteryTimeRemainingMs = -1;
+ private long mChargeTimeRemainingMs = -1;
private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
new SparseArray<>();
private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
@@ -289,6 +318,26 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Sets an approximation for how much time (in milliseconds) remains until the battery
+ * is fully discharged.
+ */
+ @NonNull
+ public Builder setBatteryTimeRemainingMs(long batteryTimeRemainingMs) {
+ mBatteryTimeRemainingMs = batteryTimeRemainingMs;
+ return this;
+ }
+
+ /**
+ * Sets an approximation for how much time (in milliseconds) remains until the battery
+ * is fully charged.
+ */
+ @NonNull
+ public Builder setChargeTimeRemainingMs(long chargeTimeRemainingMs) {
+ mChargeTimeRemainingMs = chargeTimeRemainingMs;
+ return this;
+ }
+
+ /**
* Sets the parceled recent history.
*/
@NonNull
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/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
new file mode 100644
index 000000000000..8537f47eb575
--- /dev/null
+++ b/core/java/android/os/BytesMatcher.java
@@ -0,0 +1,248 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothUuid;
+import android.net.MacAddress;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * Predicate that tests if a given {@code byte[]} value matches a set of
+ * configured rules.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader rule
+ * might accept that same value, or vice versa.
+ * <p>
+ * Matchers can contain rules of varying lengths, and tested values will only be
+ * matched against rules of the exact same length. This is designed to support
+ * {@link BluetoothUuid} style values which can be variable length.
+ *
+ * @hide
+ */
+public class BytesMatcher implements Predicate<byte[]> {
+ private static final String TAG = "BytesMatcher";
+
+ private static final char TYPE_ACCEPT = '+';
+ private static final char TYPE_REJECT = '-';
+
+ private final ArrayList<Rule> mRules = new ArrayList<>();
+
+ private static class Rule {
+ public final char type;
+ public final @NonNull byte[] value;
+ public final @Nullable byte[] mask;
+
+ public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
+ if (mask != null && value.length != mask.length) {
+ throw new IllegalArgumentException(
+ "Expected length " + value.length + " but found " + mask.length);
+ }
+ this.type = type;
+ this.value = value;
+ this.mask = mask;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ encode(builder);
+ return builder.toString();
+ }
+
+ public void encode(@NonNull StringBuilder builder) {
+ builder.append(type);
+ builder.append(HexDump.toHexString(value));
+ if (mask != null) {
+ builder.append('/');
+ builder.append(HexDump.toHexString(mask));
+ }
+ }
+
+ public boolean test(@NonNull byte[] value) {
+ if (value.length != this.value.length) {
+ return false;
+ }
+ for (int i = 0; i < this.value.length; i++) {
+ byte local = this.value[i];
+ byte remote = value[i];
+ if (this.mask != null) {
+ local &= this.mask[i];
+ remote &= this.mask[i];
+ }
+ if (local != remote) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code true} when a value being tested matches it.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_ACCEPT, value, mask));
+ }
+
+ /**
+ * Add a rule that will result in {@link #test(byte[])} returning
+ * {@code false} when a value being tested matches it.
+ * <p>
+ * Rules are tested in the order in which they were originally added, which
+ * means a narrow rule can reject a specific value before a later broader
+ * rule might accept that same value, or vice versa.
+ *
+ * @param value to be matched
+ * @param mask to be applied to both values before testing for equality; if
+ * {@code null} then both values must match exactly
+ */
+ public void addRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
+ mRules.add(new Rule(TYPE_REJECT, value, mask));
+ }
+
+ /**
+ * Test if the given {@code ParcelUuid} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
+ return test(BluetoothUuid.uuidToBytes(value));
+ }
+
+ /**
+ * Test if the given {@code MacAddress} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean testMacAddress(@NonNull MacAddress value) {
+ return test(value.toByteArray());
+ }
+
+ /**
+ * Test if the given {@code byte[]} value matches the set of rules
+ * configured in this matcher.
+ */
+ @Override
+ public boolean test(@NonNull byte[] value) {
+ return test(value, false);
+ }
+
+ /**
+ * Test if the given {@code byte[]} value matches the set of rules
+ * configured in this matcher.
+ */
+ public boolean test(@NonNull byte[] value, boolean defaultValue) {
+ final int size = mRules.size();
+ for (int i = 0; i < size; i++) {
+ final Rule rule = mRules.get(i);
+ if (rule.test(value)) {
+ return (rule.type == TYPE_ACCEPT);
+ }
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Encode the given matcher into a human-readable {@link String} which can
+ * be used to transport matchers across device boundaries.
+ * <p>
+ * The human-readable format is an ordered list separated by commas, where
+ * each rule is a {@code +} or {@code -} symbol indicating if the match
+ * should be accepted or rejected, then followed by a hex value and an
+ * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+ * encoded matcher.
+ *
+ * @see #decode(String)
+ */
+ public static @NonNull String encode(@NonNull BytesMatcher matcher) {
+ final StringBuilder builder = new StringBuilder();
+ final int size = matcher.mRules.size();
+ for (int i = 0; i < size; i++) {
+ final Rule rule = matcher.mRules.get(i);
+ rule.encode(builder);
+ builder.append(',');
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+
+ /**
+ * Decode the given human-readable {@link String} used to transport matchers
+ * across device boundaries.
+ * <p>
+ * The human-readable format is an ordered list separated by commas, where
+ * each rule is a {@code +} or {@code -} symbol indicating if the match
+ * should be accepted or rejected, then followed by a hex value and an
+ * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
+ * encoded matcher.
+ *
+ * @see #encode(BytesMatcher)
+ */
+ public static @NonNull BytesMatcher decode(@NonNull String value) {
+ final BytesMatcher matcher = new BytesMatcher();
+ final int length = value.length();
+ for (int i = 0; i < length;) {
+ final char type = value.charAt(i);
+
+ int nextRule = value.indexOf(',', i);
+ int nextMask = value.indexOf('/', i);
+
+ if (nextRule == -1) nextRule = length;
+ if (nextMask > nextRule) nextMask = -1;
+
+ final byte[] ruleValue;
+ final byte[] ruleMask;
+ if (nextMask >= 0) {
+ ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
+ ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
+ } else {
+ ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
+ ruleMask = null;
+ }
+
+ switch (type) {
+ case TYPE_ACCEPT:
+ matcher.addAcceptRule(ruleValue, ruleMask);
+ break;
+ case TYPE_REJECT:
+ matcher.addRejectRule(ruleValue, ruleMask);
+ break;
+ default:
+ Log.w(TAG, "Ignoring unknown type " + type);
+ break;
+ }
+
+ i = nextRule + 1;
+ }
+ return matcher;
+ }
+}
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/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 874add5cdbd8..91d6a9bf69cb 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -23,7 +23,6 @@ import android.net.ITetheringStatsProvider;
import android.net.Network;
import android.net.NetworkStats;
import android.net.RouteInfo;
-import android.net.UidRange;
/**
* @hide
@@ -182,11 +181,6 @@ interface INetworkManagementService
String[] listTetheredInterfaces();
/**
- * Sets the list of DNS forwarders (in order of priority)
- */
- void setDnsForwarders(in Network network, in String[] dns);
-
- /**
* Returns the list of DNS forwarders (in order of priority)
*/
String[] getDnsForwarders();
@@ -300,8 +294,6 @@ interface INetworkManagementService
void setFirewallUidRules(int chain, in int[] uids, in int[] rules);
void setFirewallChainEnabled(int chain, boolean enable);
- void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
-
/**
* Allow UID to call protect().
*/
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 8f6161329e53..ae7d94cbad79 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -42,8 +42,7 @@ interface IPowerManager
void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag);
boolean isWakeLockLevelSupported(int level);
- @UnsupportedAppUsage
- void userActivity(long time, int event, int flags);
+ void userActivity(int displayId, long time, int event, int flags);
void wakeUp(long time, int reason, String details, String opPackageName);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void goToSleep(long time, int reason, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e5163d83de69..786a7d08047e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1277,7 +1277,7 @@ public final class PowerManager {
})
public void userActivity(long when, int event, int flags) {
try {
- mService.userActivity(when, event, flags);
+ mService.userActivity(mContext.getDisplayId(), when, event, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
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/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 9ffc5aa0022c..bf2898137967 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,7 +333,7 @@ public class ZygoteProcess {
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
- * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
+ * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
* volume UUID and inode number.
* @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -359,7 +359,7 @@ public class ZygoteProcess {
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
@Nullable Map<String, Pair<String, Long>>
- whitelistedDataInfoMap,
+ allowlistedDataInfoList,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
@@ -373,7 +373,7 @@ public class ZygoteProcess {
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+ pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
bindMountAppStorageDirs, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
@@ -615,7 +615,7 @@ public class ZygoteProcess {
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
- * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
+ * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
* volume UUID and inode number.
* @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -642,7 +642,7 @@ public class ZygoteProcess {
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
@Nullable Map<String, Pair<String, Long>>
- whitelistedDataInfoMap,
+ allowlistedDataInfoList,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
@@ -733,12 +733,12 @@ public class ZygoteProcess {
}
argsForZygote.add(sb.toString());
}
- if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+ if (allowlistedDataInfoList != null && allowlistedDataInfoList.size() > 0) {
StringBuilder sb = new StringBuilder();
- sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+ sb.append(Zygote.ALLOWLISTED_DATA_INFO_MAP);
sb.append("=");
boolean started = false;
- for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+ for (Map.Entry<String, Pair<String, Long>> entry : allowlistedDataInfoList.entrySet()) {
if (started) {
sb.append(',');
}
@@ -1318,7 +1318,7 @@ public class ZygoteProcess {
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
- null /* whitelistedDataInfoMap */, true /* bindMountAppsData*/,
+ null /* allowlistedDataInfoList */, true /* bindMountAppsData*/,
/* bindMountAppStorageDirs */ false, extraArgs);
} catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 592e98abae63..dc6f63a94685 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -58,6 +58,8 @@ public final class IncrementalManager {
private static final String ALLOWED_PROPERTY = "incremental.allowed";
+ public static final int MIN_VERSION_TO_SUPPORT_FSVERITY = 2;
+
public static final int CREATE_MODE_TEMPORARY_BIND =
IIncrementalService.CREATE_MODE_TEMPORARY_BIND;
public static final int CREATE_MODE_PERMANENT_BIND =
@@ -155,22 +157,21 @@ public final class IncrementalManager {
}
/**
- * Set up an app's code path. The expected outcome of this method is:
+ * Link an app's files from the stage dir to the final installation location.
+ * The expected outcome of this method is:
* 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory
* of {@code afterCodeFile}.
* 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}.
*
* @param beforeCodeFile Path that is currently bind-mounted and have APKs under it.
- * Should no longer have any APKs after this method is called.
* Example: /data/app/vmdl*tmp
* @param afterCodeFile Path that should will have APKs after this method is called. Its parent
* directory should be bind-mounted to a directory under /data/incremental.
* Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB]
* @throws IllegalArgumentException
* @throws IOException
- * TODO(b/147371381): add unit tests
*/
- public void renameCodePath(File beforeCodeFile, File afterCodeFile)
+ public void linkCodePath(File beforeCodeFile, File afterCodeFile)
throws IllegalArgumentException, IOException {
final File beforeCodeAbsolute = beforeCodeFile.getAbsoluteFile();
final IncrementalStorage apkStorage = openStorage(beforeCodeAbsolute.toString());
@@ -188,7 +189,6 @@ public final class IncrementalManager {
try {
final String afterCodePathName = afterCodeFile.getName();
linkFiles(apkStorage, beforeCodeAbsolute, "", linkedApkStorage, afterCodePathName);
- apkStorage.unBind(beforeCodeAbsolute.toString());
} catch (Exception e) {
linkedApkStorage.unBind(targetStorageDir);
throw e;
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 0041699df9ef..98b4e0b4f402 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -28,6 +28,8 @@ import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import com.android.internal.os.AppFuseMount;
+import android.app.PendingIntent;
+
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
@@ -198,4 +200,5 @@ interface IStorageManager {
void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90;
void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91;
void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92;
+ PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 93;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7c8874cc1ea7..c967deb5e810 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -49,6 +49,7 @@ import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -701,6 +702,33 @@ public class StorageManager {
}
}
+ /**
+ * Returns a {@link PendingIntent} that can be used by Apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission
+ * to launch the manageSpaceActivity for any App that implements it, irrespective of its
+ * exported status.
+ * <p>
+ * Caller has the responsibility of supplying a valid packageName which has
+ * manageSpaceActivity implemented.
+ *
+ * @param packageName package name for the App for which manageSpaceActivity is to be launched
+ * @param requestCode for launching the activity
+ * @return PendingIntent to launch the manageSpaceActivity if successful, null if the
+ * packageName doesn't have a manageSpaceActivity.
+ * @throws IllegalArgumentException an invalid packageName is supplied.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+ @Nullable
+ public PendingIntent getManageSpaceActivityIntent(
+ @NonNull String packageName, int requestCode) {
+ try {
+ return mStorageManager.getManageSpaceActivityIntent(packageName,
+ requestCode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private ObbInfo getObbInfo(String canonicalPath) {
try {
final ObbInfo obbInfo = ObbScanner.getObbInfo(canonicalPath);
@@ -2738,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();
}
@@ -2764,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/OWNERS b/core/java/android/permission/OWNERS
index b32346848a69..19a3a8bd514a 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,7 +1,13 @@
# Bug component: 137825
+eugenesusla@google.com
evanseverson@google.com
+evanxinchen@google.com
+ewol@google.com
+guojing@google.com
+jaysullivan@google.com
ntmyren@google.com
-zhanghai@google.com
svetoslavganov@android.com
svetoslavganov@google.com
+theianchen@google.com
+zhanghai@google.com
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/permissionpresenterservice/OWNERS b/core/java/android/permissionpresenterservice/OWNERS
index b32346848a69..fb6099cf7e5a 100644
--- a/core/java/android/permissionpresenterservice/OWNERS
+++ b/core/java/android/permissionpresenterservice/OWNERS
@@ -1,7 +1,3 @@
# Bug component: 137825
-evanseverson@google.com
-ntmyren@google.com
-zhanghai@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 617220e00f9d..85cef84d8d30 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16564,30 +16564,6 @@ public final class Settings {
/**
* Performs a strict and comprehensive check of whether a calling package is allowed to
- * change the state of network, as the condition differs for pre-M, M+, and
- * privileged/preinstalled apps. The caller is expected to have either the
- * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
- * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
- * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
- * permission and cannot be revoked. See http://b/23597341
- *
- * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
- * of this app will be updated to the current time.
- * @hide
- */
- public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid,
- String callingPackage, String callingAttributionTag, boolean throwException) {
- if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
- callingPackage, callingAttributionTag, throwException,
- AppOpsManager.OP_WRITE_SETTINGS, PM_CHANGE_NETWORK_STATE, true);
- }
-
- /**
- * Performs a strict and comprehensive check of whether a calling package is allowed to
* draw on top of other apps, as the conditions differs for pre-M, M+, and
* privileged/preinstalled apps. If the provided uid does not match the callingPackage,
* a negative result will be returned.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 7996f090b1a4..8a4812a42c8a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5302,5 +5302,13 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_RCS_CONFIG = "rcs_config";
+
+ /**
+ * TelephonyProvider column name for VoIMS provisioning. Default is 0.
+ * <P>Type: INTEGER </P>
+ *
+ * @hide
+ */
+ public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
}
}
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index fbc25a6aaf74..4c8ee598f512 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -18,10 +18,13 @@ package android.service.autofill;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -44,6 +47,8 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -76,6 +81,8 @@ public final class AutofillServiceInfo {
@Nullable
private final String mSettingsActivity;
+ @Nullable
+ private final String mPasswordsActivity;
@Nullable
private final ArrayMap<String, Long> mCompatibilityPackages;
@@ -113,12 +120,14 @@ public final class AutofillServiceInfo {
AutofillService.SERVICE_META_DATA);
if (parser == null) {
mSettingsActivity = null;
+ mPasswordsActivity = null;
mCompatibilityPackages = null;
mInlineSuggestionsEnabled = false;
return;
}
String settingsActivity = null;
+ String passwordsActivity = null;
ArrayMap<String, Long> compatibilityPackages = null;
boolean inlineSuggestionsEnabled = false; // false by default.
@@ -139,6 +148,8 @@ public final class AutofillServiceInfo {
com.android.internal.R.styleable.AutofillService);
settingsActivity = afsAttributes.getString(
R.styleable.AutofillService_settingsActivity);
+ passwordsActivity = afsAttributes.getString(
+ R.styleable.AutofillService_passwordsActivity);
inlineSuggestionsEnabled = afsAttributes.getBoolean(
R.styleable.AutofillService_supportsInlineSuggestions, false);
} finally {
@@ -155,6 +166,7 @@ public final class AutofillServiceInfo {
}
mSettingsActivity = settingsActivity;
+ mPasswordsActivity = passwordsActivity;
mCompatibilityPackages = compatibilityPackages;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
}
@@ -221,6 +233,7 @@ public final class AutofillServiceInfo {
return compatibilityPackages;
}
+ @NonNull
public ServiceInfo getServiceInfo() {
return mServiceInfo;
}
@@ -230,6 +243,12 @@ public final class AutofillServiceInfo {
return mSettingsActivity;
}
+ @Nullable
+ public String getPasswordsActivity() {
+ return mPasswordsActivity;
+ }
+
+ @Nullable
public ArrayMap<String, Long> getCompatibilityPackages() {
return mCompatibilityPackages;
}
@@ -238,12 +257,37 @@ public final class AutofillServiceInfo {
return mInlineSuggestionsEnabled;
}
+ /**
+ * Queries the valid autofill services available for the user.
+ */
+ public static List<AutofillServiceInfo> getAvailableServices(
+ Context context, @UserIdInt int user) {
+ final List<AutofillServiceInfo> services = new ArrayList<>();
+
+ final List<ResolveInfo> resolveInfos =
+ context.getPackageManager().queryIntentServicesAsUser(
+ new Intent(AutofillService.SERVICE_INTERFACE),
+ PackageManager.GET_META_DATA,
+ user);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ try {
+ services.add(new AutofillServiceInfo(context, serviceInfo));
+ } catch (SecurityException e) {
+ // Service does not declare the proper permission, ignore it.
+ Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
+ }
+ }
+ return services;
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append("[").append(mServiceInfo);
builder.append(", settings:").append(mSettingsActivity);
+ builder.append(", passwords activity:").append(mPasswordsActivity);
builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
&& !mCompatibilityPackages.isEmpty()).append("]");
builder.append(", inline suggestions enabled:").append(mInlineSuggestionsEnabled);
@@ -256,6 +300,7 @@ public final class AutofillServiceInfo {
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
+ pw.print(prefix); pw.print("Passwords activity: "); pw.println(mPasswordsActivity);
pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
pw.print(prefix); pw.print("Inline Suggestions Enabled: ");
pw.println(mInlineSuggestionsEnabled);
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/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e7ceada6180a..e37921ec03cc 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -17,10 +17,7 @@
package android.telephony;
import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.annotation.ChangeId;
@@ -31,16 +28,12 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.DisconnectCauses;
-import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
-import android.telephony.NetworkRegistrationInfo.Domain;
import android.telephony.TelephonyManager.DataEnabledReason;
-import android.telephony.TelephonyManager.DataState;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
@@ -49,8 +42,6 @@ import com.android.internal.telephony.IPhoneStateListener;
import dalvik.system.VMRuntime;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
@@ -71,49 +62,15 @@ import java.util.concurrent.Executor;
* information unless it has the appropriate permissions declared in
* its manifest file. Where permissions apply, they are noted in the
* appropriate LISTEN_ flags.
+ *
+ * @deprecated Use {@link TelephonyCallback} instead.
*/
+@Deprecated
public class PhoneStateListener {
private static final String LOG_TAG = "PhoneStateListener";
private static final boolean DBG = false; // STOPSHIP if true
/**
- * Experiment flag to set the per-pid registration limit for PhoneStateListeners
- *
- * Limit on registrations of {@link PhoneStateListener}s on a per-pid
- * basis. When this limit is exceeded, any calls to {@link TelephonyManager#listen} will fail
- * with an {@link IllegalStateException}.
- *
- * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
- * TelephonyRegistry runs under are exempt from this limit.
- *
- * If the value of the flag is less than 1, enforcement of the limit will be disabled.
- * @hide
- */
- public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
- "phone_state_listener_per_pid_registration_limit";
-
- /**
- * Default value for the per-pid registation limit.
- * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
- * @hide
- */
- public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
-
- /**
- * This change enables a limit on the number of {@link PhoneStateListener} objects any process
- * may register via {@link TelephonyManager#listen}. The default limit is 50, which may change
- * via remote device config updates.
- *
- * This limit is enforced via an {@link IllegalStateException} thrown from
- * {@link TelephonyManager#listen} when the offending process attempts to register one too many
- * listeners.
- *
- * @hide
- */
- @ChangeId
- public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
-
- /**
* Stop listening for updates.
*
* The PhoneStateListener is not tied to any subscription and unregistered for any update.
@@ -125,7 +82,7 @@ public class PhoneStateListener {
*
* @see #onServiceStateChanged
* @see ServiceState
- * @deprecated Use {@link ServiceStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
*/
@Deprecated
public static final int LISTEN_SERVICE_STATE = 0x00000001;
@@ -135,7 +92,7 @@ public class PhoneStateListener {
* {@more}
*
* @see #onSignalStrengthChanged
- * @deprecated Use {@link SignalStrengthsChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
*/
@Deprecated
public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002;
@@ -151,7 +108,7 @@ public class PhoneStateListener {
* voicemail icon.
*
* @see #onMessageWaitingIndicatorChanged
- * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
*/
@Deprecated
public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
@@ -164,7 +121,7 @@ public class PhoneStateListener {
* {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onCallForwardingIndicatorChanged
- * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
*/
@Deprecated
public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
@@ -182,7 +139,7 @@ public class PhoneStateListener {
* instead.
*
* @see #onCellLocationChanged
- * @deprecated Use {@link CellLocationChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
*/
@Deprecated
public static final int LISTEN_CELL_LOCATION = 0x00000010;
@@ -192,7 +149,7 @@ public class PhoneStateListener {
* {@more}
*
* @see #onCallStateChanged
- * @deprecated Use {@link CallStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
*/
@Deprecated
public static final int LISTEN_CALL_STATE = 0x00000020;
@@ -201,7 +158,7 @@ public class PhoneStateListener {
* Listen for changes to the data connection state (cellular).
*
* @see #onDataConnectionStateChanged
- * @deprecated Use {@link DataConnectionStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
*/
@Deprecated
public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
@@ -214,7 +171,7 @@ public class PhoneStateListener {
* data-traffic icon.
*
* @see #onDataActivity
- * @deprecated Use {@link DataActivityListener} instead.
+ * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
*/
@Deprecated
public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
@@ -226,7 +183,7 @@ public class PhoneStateListener {
* icon.
*
* @see #onSignalStrengthsChanged
- * @deprecated Use {@link SignalStrengthsChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
*/
@Deprecated
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
@@ -238,7 +195,8 @@ public class PhoneStateListener {
* @see #onSignalStrengthsChanged
*
* @hide
- * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.AlwaysReportedSignalStrengthListener}
+ * instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
@@ -251,7 +209,7 @@ public class PhoneStateListener {
* permission.
*
* @see #onCellInfoChanged
- * @deprecated Use {@link CellInfoChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
*/
@Deprecated
public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -265,7 +223,7 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @hide
- * @deprecated Use {@link PreciseCallStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -280,7 +238,7 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onPreciseDataConnectionStateChanged
- * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -306,7 +264,7 @@ public class PhoneStateListener {
*
* @see #onServiceStateChanged(ServiceState)
* @hide
- * @deprecated Use {@link SrvccStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
*/
@Deprecated
@SystemApi
@@ -328,7 +286,7 @@ public class PhoneStateListener {
*
* @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
* @hide
- * @deprecated Use {@link CarrierNetworkChangeListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
*/
@Deprecated
public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
@@ -349,7 +307,7 @@ public class PhoneStateListener {
*
* @see #onVoiceActivationStateChanged
* @hide
- * @deprecated Use {@link VoiceActivationStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
*/
@Deprecated
@SystemApi
@@ -369,7 +327,7 @@ public class PhoneStateListener {
*
* @see #onDataActivationStateChanged
* @hide
- * @deprecated Use {@link DataActivationStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
*/
@Deprecated
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
@@ -378,7 +336,7 @@ public class PhoneStateListener {
* Listen for changes to the user mobile data state
*
* @see #onUserMobileDataStateChanged
- * @deprecated Use {@link UserMobileDataStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
*/
@Deprecated
public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
@@ -391,7 +349,7 @@ public class PhoneStateListener {
* {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onDisplayInfoChanged
- * @deprecated Use {@link DisplayInfoChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
*/
@Deprecated
public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
@@ -401,7 +359,7 @@ public class PhoneStateListener {
*
* @see #onPhoneCapabilityChanged
* @hide
- * @deprecated Use {@link PhoneCapabilityChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
*/
@Deprecated
public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
@@ -413,7 +371,7 @@ public class PhoneStateListener {
* subscription user selected as default data subscription in DSDS mode.
*
* @see #onActiveDataSubscriptionIdChanged
- * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
*/
@Deprecated
public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
@@ -423,7 +381,7 @@ public class PhoneStateListener {
*
* @see #onRadioPowerStateChanged
* @hide
- * @deprecated Use {@link RadioPowerStateChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
*/
@Deprecated
@SystemApi
@@ -436,7 +394,7 @@ public class PhoneStateListener {
* <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
* app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
- * @deprecated Use {@link EmergencyNumberListChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
*/
@Deprecated
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
@@ -449,7 +407,7 @@ public class PhoneStateListener {
* or the calling app has carrier privileges
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
- * @deprecated Use {@link CallDisconnectCauseChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -464,7 +422,7 @@ public class PhoneStateListener {
*
* @see #onCallAttributesChanged
* @hide
- * @deprecated Use {@link CallAttributesChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
*/
@Deprecated
@SystemApi
@@ -480,7 +438,7 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
- * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -491,7 +449,7 @@ public class PhoneStateListener {
*
* @see #onOutgoingEmergencyCall
* @hide
- * @deprecated Use {@link OutgoingEmergencyCallListener} instead.
+ * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
*/
@Deprecated
@SystemApi
@@ -503,7 +461,7 @@ public class PhoneStateListener {
*
* @see #onOutgoingEmergencySms
* @hide
- * @deprecated Use {@link OutgoingEmergencySmsListener} instead.
+ * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
*/
@Deprecated
@SystemApi
@@ -524,7 +482,7 @@ public class PhoneStateListener {
* of whether the calling app has carrier privileges.
*
* @see #onRegistrationFailed
- * @deprecated Use {@link RegistrationFailedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
*/
@Deprecated
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -540,540 +498,12 @@ public class PhoneStateListener {
* of whether the calling app has carrier privileges.
*
* @see #onBarringInfoChanged
- * @deprecated Use {@link BarringInfoChangedListener} instead.
+ * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
*/
@Deprecated
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_BARRING_INFO = 0x80000000;
- /**
- * Event for changes to the network service state (cellular).
- *
- * @see ServiceStateChangedListener#onServiceStateChanged
- * @see ServiceState
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_SERVICE_STATE_CHANGED = 1;
-
- /**
- * Event for changes to the network signal strength (cellular).
- *
- * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
-
- /**
- * Event for changes to the message-waiting indicator.
- *
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
- * the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}).
- * <p>
- * Example: The status bar uses this to determine when to display the
- * voicemail icon.
- *
- * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
-
- /**
- * Event for changes to the call-forwarding indicator.
- *
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
- * the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
-
- /**
- * Event for changes to the device's cell location. Note that
- * this will result in frequent callbacks to the listener.
- *
- * If you need regular location updates but want more control over
- * the update interval or location precision, you can set up a listener
- * through the {@link android.location.LocationManager location manager}
- * instead.
- *
- * @see CellLocationChangedListener#onCellLocationChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
- public static final int EVENT_CELL_LOCATION_CHANGED = 5;
-
- /**
- * Event for changes to the device call state.
- *
- * @see CallStateChangedListener#onCallStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
- public static final int EVENT_CALL_STATE_CHANGED = 6;
-
- /**
- * Event for changes to the data connection state (cellular).
- *
- * @see DataConnectionStateChangedListener#onDataConnectionStateChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
-
- /**
- * Event for changes to the direction of data traffic on the data
- * connection (cellular).
- *
- * Example: The status bar uses this to display the appropriate
- * data-traffic icon.
- *
- * @see DataActivityListener#onDataActivity
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
-
- /**
- * Event for changes to the network signal strengths (cellular).
- * <p>
- * Example: The status bar uses this to control the signal-strength
- * icon.
- *
- * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
-
- /**
- * Event for changes of the network signal strengths (cellular) always reported from modem,
- * even in some situations such as the screen of the device is off.
- *
- * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
-
- /**
- * Event for changes to observed cell info.
- *
- * @see CellInfoChangedListener#onCellInfoChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
- public static final int EVENT_CELL_INFO_CHANGED = 11;
-
- /**
- * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
- * background and foreground calls.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see PreciseCallStateChangedListener#onPreciseCallStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
-
- /**
- * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
-
- /**
- * Event for real time info for all data connections (cellular)).
- *
- * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
- *
- * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
- * @hide
- */
- @Deprecated
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
-
- /**
- * Event for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int EVENT_OEM_HOOK_RAW = 15;
-
- /**
- * Event for changes to the SRVCC state of the active call.
- *
- * @see SrvccStateChangedListener#onSrvccStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int EVENT_SRVCC_STATE_CHANGED = 16;
-
- /**
- * Event for carrier network changes indicated by a carrier app.
- *
- * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
- * @see CarrierNetworkChangeListener#onCarrierNetworkChange
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
-
- /**
- * Event for changes to the sim voice activation state
- *
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- *
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
- * fully activated
- *
- * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
-
- /**
- * Event for changes to the sim data activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- *
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
- * fully activated
- *
- * @see DataActivationStateChangedListener#onDataActivationStateChanged
- * @hide
- */
- @SystemApi
- public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
-
- /**
- * Event for changes to the user mobile data state
- *
- * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
-
- /**
- * Event for display info changed event.
- *
- * @see DisplayInfoChangedListener#onDisplayInfoChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
-
- /**
- * Event for changes to the phone capability.
- *
- * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged
- *
- * @hide
- */
- @SystemApi
- public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
-
- /**
- * Event for changes to active data subscription ID. Active data subscription is
- * the current subscription used to setup Cellular Internet data. For example,
- * it could be the current active opportunistic subscription in use, or the
- * subscription user selected as default data subscription in DSDS mode.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
-
- /**
- * Event for changes to the radio power state.
- *
- * @see RadioPowerStateChangedListener#onRadioPowerStateChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
-
- /**
- * Event for changes to emergency number list based on all active subscriptions.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
-
- /**
- * Event for call disconnect causes which contains {@link DisconnectCause} and
- * {@link PreciseDisconnectCause}.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
-
- /**
- * Event for changes to the call attributes of a currently active call.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see CallAttributesChangedListener#onCallAttributesChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
-
- /**
- * Event for IMS call disconnect causes which contains
- * {@link android.telephony.ims.ImsReasonInfo}
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
-
- /**
- * Event for the emergency number placed from an outgoing call.
- *
- * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
-
- /**
- * Event for the emergency number placed from an outgoing SMS.
- *
- * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
-
- /**
- * Event for registration failures.
- *
- * Event for indications that a registration procedure has failed in either the CS or PS
- * domain. This indication does not necessarily indicate a change of service state, which should
- * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
- * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
- * of whether the calling app has carrier privileges.
- *
- * @see RegistrationFailedListener#onRegistrationFailed
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PRECISE_PHONE_STATE,
- Manifest.permission.ACCESS_FINE_LOCATION
- })
- public static final int EVENT_REGISTRATION_FAILURE = 31;
-
- /**
- * Event for Barring Information for the current registered / camped cell.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
- * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
- * of whether the calling app has carrier privileges.
- *
- * @see BarringInfoChangedListener#onBarringInfoChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PRECISE_PHONE_STATE,
- Manifest.permission.ACCESS_FINE_LOCATION
- })
- public static final int EVENT_BARRING_INFO_CHANGED = 32;
-
- /**
- * Event for changes to the physical channel configuration.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
-
-
- /**
- * Event for changes to the data enabled.
- *
- * Event for indications that the enabled status of current data has changed.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see DataEnabledChangedListener#onDataEnabledChanged
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final int EVENT_DATA_ENABLED_CHANGED = 34;
-
- /**
- * Event for changes to allowed network list based on all active subscriptions.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @hide
- * @see AllowedNetworkTypesChangedListener#onAllowedNetworkTypesChanged
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
-
- /** @hide */
- @IntDef(prefix = { "EVENT_" }, value = {
- EVENT_SERVICE_STATE_CHANGED,
- EVENT_SIGNAL_STRENGTH_CHANGED,
- EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
- EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
- EVENT_CELL_LOCATION_CHANGED,
- EVENT_CALL_STATE_CHANGED,
- EVENT_DATA_CONNECTION_STATE_CHANGED,
- EVENT_DATA_ACTIVITY_CHANGED,
- EVENT_SIGNAL_STRENGTHS_CHANGED,
- EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
- EVENT_CELL_INFO_CHANGED,
- EVENT_PRECISE_CALL_STATE_CHANGED,
- EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
- EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
- EVENT_OEM_HOOK_RAW,
- EVENT_SRVCC_STATE_CHANGED,
- EVENT_CARRIER_NETWORK_CHANGED,
- EVENT_VOICE_ACTIVATION_STATE_CHANGED,
- EVENT_DATA_ACTIVATION_STATE_CHANGED,
- EVENT_USER_MOBILE_DATA_STATE_CHANGED,
- EVENT_DISPLAY_INFO_CHANGED,
- EVENT_PHONE_CAPABILITY_CHANGED,
- EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
- EVENT_RADIO_POWER_STATE_CHANGED,
- EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
- EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
- EVENT_CALL_ATTRIBUTES_CHANGED,
- EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
- EVENT_OUTGOING_EMERGENCY_CALL,
- EVENT_OUTGOING_EMERGENCY_SMS,
- EVENT_REGISTRATION_FAILURE,
- EVENT_BARRING_INFO_CHANGED,
- EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
- EVENT_DATA_ENABLED_CHANGED,
- EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TelephonyEvent {}
-
/*
* Subscription used to listen to the phone state changes
* @hide
@@ -1085,19 +515,16 @@ public class PhoneStateListener {
/**
* @hide
*/
- //TODO: The maxTargetSdk should be S if the build time tool updates it.
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@UnsupportedAppUsage(
maxTargetSdk = Build.VERSION_CODES.R,
- publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" +
- "Executor, PhoneStateListener)} instead")
- public IPhoneStateListener callback;
+ publicAlternatives = "Use {@code TelephonyManager#registerTelephonyCallback(" +
+ "Executor, TelephonyCallback)} instead")
+ public final IPhoneStateListener callback;
/**
* Create a PhoneStateListener for the Phone with the default subscription.
- * If this is created for use with deprecated API
- * {@link TelephonyManager#listen(PhoneStateListener, int)}, then this class requires
- * Looper.myLooper() not return null.
+ * This class requires Looper.myLooper() not return null.
*/
public PhoneStateListener() {
this(null, Looper.myLooper());
@@ -1135,10 +562,7 @@ public class PhoneStateListener {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public PhoneStateListener(Integer subId, Looper looper) {
- if (looper != null) {
- setExecutor(new HandlerExecutor(new Handler(looper)));
- }
- mSubId = subId;
+ this(subId, new HandlerExecutor(new Handler(looper)));
if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
>= Build.VERSION_CODES.Q) {
throw new IllegalArgumentException("PhoneStateListener with subId: "
@@ -1153,783 +577,18 @@ public class PhoneStateListener {
* The Executor must not be null.
*
* @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
- * @deprecated Use
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead.
*/
@Deprecated
public PhoneStateListener(@NonNull Executor executor) {
- setExecutor(executor);
- mSubId = null;
+ this(null, executor);
}
- private @NonNull Executor mExecutor;
-
- /**
- * @hide
- */
- public void setExecutor(@NonNull @CallbackExecutor Executor executor) {
- if (executor == null) {
+ private PhoneStateListener(Integer subId, Executor e) {
+ if (e == null) {
throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
}
- mExecutor = executor;
- callback = new IPhoneStateListenerStub(this, mExecutor);
- }
-
- /**
- * @hide
- */
- public boolean isExecutorSet() {
- return mExecutor != null;
- }
-
- /**
- * Interface for service state listener.
- */
- public interface ServiceStateChangedListener {
- /**
- * Callback invoked when device service state changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * The instance of {@link ServiceState} passed as an argument here will have various
- * levels of location information stripped from it depending on the location permissions
- * that your app holds.
- * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
- * receive all the information in {@link ServiceState}.
- *
- * @see ServiceState#STATE_EMERGENCY_ONLY
- * @see ServiceState#STATE_IN_SERVICE
- * @see ServiceState#STATE_OUT_OF_SERVICE
- * @see ServiceState#STATE_POWER_OFF
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onServiceStateChanged(@NonNull ServiceState serviceState);
- }
-
- /**
- * Interface for message waiting indicator listener.
- */
- public interface MessageWaitingIndicatorChangedListener {
- /**
- * Callback invoked when the message-waiting indicator changes on the registered
- * subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void onMessageWaitingIndicatorChanged(boolean mwi);
- }
-
- /**
- * Interface for call-forwarding indicator listener.
- */
- public interface CallForwardingIndicatorChangedListener {
- /**
- * Callback invoked when the call-forwarding indicator changes on the registered
- * subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void onCallForwardingIndicatorChanged(boolean cfi);
- }
-
- /**
- * Interface for device cell location listener.
- */
- public interface CellLocationChangedListener {
- /**
- * Callback invoked when device cell location changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
- public void onCellLocationChanged(@NonNull CellLocation location);
- }
-
- /**
- * Interface for call state listener.
- */
- public interface CallStateChangedListener {
- /**
- * Callback invoked when device call state changes.
- * <p>
- * Reports the state of Telephony (mobile) calls on the device for the registered s
- * ubscription.
- * <p>
- * Note: the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- * <p>
- * Note: The state returned here may differ from that returned by
- * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
- * calling {@link TelephonyManager#getCallState()} from within this callback may return a
- * different state than the callback reports.
- *
- * @param state call state
- * @param phoneNumber call phone number. If application does not have
- * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier
- * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
- * passed as an argument.
- */
- @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
- public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber);
- }
-
- /**
- * Interface for data connection state listener.
- */
- public interface DataConnectionStateChangedListener {
- /**
- * Callback invoked when connection state changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see TelephonyManager#DATA_DISCONNECTED
- * @see TelephonyManager#DATA_CONNECTING
- * @see TelephonyManager#DATA_CONNECTED
- * @see TelephonyManager#DATA_SUSPENDED
- *
- * @param state is the current state of data connection.
- * @param networkType is the current network type of data connection.
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onDataConnectionStateChanged(@DataState int state,
- @NetworkType int networkType);
- }
-
- /**
- * Interface for data activity state listener.
- */
- public interface DataActivityListener {
- /**
- * Callback invoked when data activity state changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see TelephonyManager#DATA_ACTIVITY_NONE
- * @see TelephonyManager#DATA_ACTIVITY_IN
- * @see TelephonyManager#DATA_ACTIVITY_OUT
- * @see TelephonyManager#DATA_ACTIVITY_INOUT
- * @see TelephonyManager#DATA_ACTIVITY_DORMANT
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onDataActivity(@DataActivityType int direction);
- }
-
- /**
- * Interface for network signal strengths listener.
- */
- public interface SignalStrengthsChangedListener {
- /**
- * Callback invoked when network signal strengths changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
- }
-
- /**
- * Interface for network signal strengths listener which always reported from modem.
- */
- public interface AlwaysReportedSignalStrengthChangedListener {
- /**
- * Callback always invoked from modem when network signal strengths changes on the
- * registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
- }
-
- /**
- * Interface for cell info listener.
- */
- public interface CellInfoChangedListener {
- /**
- * Callback invoked when a observed cell info has changed or new cells have been added
- * or removed on the registered subscription.
- * Note, the registration subscription ID s from {@link TelephonyManager} object
- * which registersPhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param cellInfo is the list of currently visible cells.
- */
- @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
- public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
- }
-
- /**
- * Interface for precise device call state listener.
- *
- * @hide
- */
- @SystemApi
- public interface PreciseCallStateChangedListener {
- /**
- * Callback invoked when precise device call state changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param callState {@link PreciseCallState}
- */
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
- }
-
- /**
- * Interface for call disconnect cause listener.
- */
- public interface CallDisconnectCauseChangedListener {
- /**
- * Callback invoked when call disconnect cause changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param disconnectCause {@link DisconnectCause}.
- * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
- */
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
- @PreciseDisconnectCauses int preciseDisconnectCause);
- }
-
- /**
- * Interface for IMS call disconnect cause listener.
- */
- public interface ImsCallDisconnectCauseChangedListener {
- /**
- * Callback invoked when IMS call disconnect cause changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
- *
- */
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
- }
-
- /**
- * Interface for precise data connection state listener.
- */
- public interface PreciseDataConnectionStateChangedListener {
- /**
- * Callback providing update about the default/internet data connection on the registered
- * subscription.
- *
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
- * or the calling app has carrier privileges
- * (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @param dataConnectionState {@link PreciseDataConnectionState}
- */
- @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onPreciseDataConnectionStateChanged(
- @NonNull PreciseDataConnectionState dataConnectionState);
- }
-
- /**
- * Interface for Single Radio Voice Call Continuity listener.
- *
- * @hide
- */
- @SystemApi
- public interface SrvccStateChangedListener {
- /**
- * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
- * (SRVCC) state for the currently active call on the registered subscription.
- *
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void onSrvccStateChanged(@SrvccState int srvccState);
- }
-
- /**
- * Interface for SIM voice activation state listener.
- *
- * @hide
- */
- @SystemApi
- public interface VoiceActivationStateChangedListener {
- /**
- * Callback invoked when the SIM voice activation state has changed on the registered
- * subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param state is the current SIM voice activation state
- */
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void onVoiceActivationStateChanged(@SimActivationState int state);
-
- }
-
- /**
- * Interface for SIM data activation state listener.
- */
- public interface DataActivationStateChangedListener {
- /**
- * Callback invoked when the SIM data activation state has changed on the registered
- * subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param state is the current SIM data activation state
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onDataActivationStateChanged(@SimActivationState int state);
- }
-
- /**
- * Interface for user mobile data state listener.
- */
- public interface UserMobileDataStateChangedListener {
- /**
- * Callback invoked when the user mobile data state has changed on the registered
- * subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param enabled indicates whether the current user mobile data state is enabled or
- * disabled.
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onUserMobileDataStateChanged(boolean enabled);
- }
-
- /**
- * Interface for display info listener.
- */
- public interface DisplayInfoChangedListener {
- /**
- * Callback invoked when the display info has changed on the registered subscription.
- * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
- * based on carrier policy.
- *
- * @param telephonyDisplayInfo The display information.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
- }
-
- /**
- * Interface for the current emergency number list listener.
- */
- public interface EmergencyNumberListChangedListener {
- /**
- * Callback invoked when the current emergency number list has changed on the registered
- * subscription.
- *
- * Note, the registered subscription is associated with {@link TelephonyManager} object
- * on which
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
- * was called.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * given subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param emergencyNumberList Map associating all active subscriptions on the device with
- * the list of emergency numbers originating from that
- * subscription.
- * If there are no active subscriptions, the map will contain a
- * single entry with
- * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
- * the key and a list of emergency numbers as the value. If no
- * emergency number information is available, the value will be
- * empty.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void onEmergencyNumberListChanged(
- @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
- }
-
- /**
- * Interface for outgoing emergency call listener.
- *
- * @hide
- */
- @SystemApi
- public interface OutgoingEmergencyCallListener {
- /**
- * Callback invoked when an outgoing call is placed to an emergency number.
- *
- * This method will be called when an emergency call is placed on any subscription
- * (including the no-SIM case), regardless of which subscription this listener was
- * registered on.
- *
- * The default implementation of this method calls
- * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes.
- * Do not call {@code super(...)} from within your implementation unless you want
- * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
- *
- * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
- * placed to.
- * @param subscriptionId The subscription ID used to place the emergency call. If the
- * emergency call was placed without a valid subscription
- * (e.g. when there are no SIM cards in the device), this will be
- * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
- */
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
- int subscriptionId);
- }
-
- /**
- * Interface for outgoing emergency sms listener.
- *
- * @hide
- */
- @SystemApi
- public interface OutgoingEmergencySmsListener {
- /**
- * Smsback invoked when an outgoing sms is sent to an emergency number.
- *
- * This method will be called when an emergency sms is sent on any subscription,
- * regardless of which subscription this listener was registered on.
- *
- * The default implementation of this method calls
- * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
- * not call {@code super(...)} from within your implementation unless you want
- * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
- *
- * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
- * @param subscriptionId The subscription ID used to send the emergency sms.
- */
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
- int subscriptionId);
- }
-
- /**
- * Interface for phone capability listener.
- * @hide
- */
- @SystemApi
- public interface PhoneCapabilityChangedListener {
- /**
- * Callback invoked when phone capability changes.
- * Note, this callback triggers regardless of registered subscription.
- *
- * @param capability the new phone capability
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
- }
-
- /**
- * Interface for active data subscription ID listener.
- */
- public interface ActiveDataSubscriptionIdChangedListener {
- /**
- * Callback invoked when active data subscription ID changes.
- * Note, this callback triggers regardless of registered subscription.
- *
- * @param subId current subscription used to setup Cellular Internet data.
- * For example, it could be the current active opportunistic subscription
- * in use, or the subscription user selected as default data subscription in
- * DSDS mode.
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public void onActiveDataSubscriptionIdChanged(int subId);
- }
-
- /**
- * Interface for modem radio power state listener.
- *
- * @hide
- */
- @SystemApi
- public interface RadioPowerStateChangedListener {
- /**
- * Callback invoked when modem radio power state changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param state the modem radio power state
- */
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void onRadioPowerStateChanged(@RadioPowerState int state);
- }
-
- /**
- * Interface for carrier network listener.
- */
- public interface CarrierNetworkChangeListener {
- /**
- * Callback invoked when telephony has received notice from a carrier
- * app that a network action that could result in connectivity loss
- * has been requested by an app using
- * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
- *
- * This is optional and is only used to allow the system to provide alternative UI while
- * telephony is performing an action that may result in intentional, temporary network
- * lack of connectivity.
- *
- * Note, this callback is pinned to the registered subscription and will be invoked when
- * the notifying carrier app has carrier privilege rule on the registered
- * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
- *
- * @param active If the carrier network change is or shortly will be active,
- * {@code true} indicate that showing alternative UI, {@code false} otherwise.
- */
- public void onCarrierNetworkChange(boolean active);
- }
-
- /**
- * Interface for registration failures listener.
- */
- public interface RegistrationFailedListener {
- /**
- * Report that Registration or a Location/Routing/Tracking Area update has failed.
- *
- * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
- * area update fails. This includes procedures that do not necessarily result in a change of
- * the modem's registration status. If the modem's registration status changes, that is
- * reflected in the onNetworkStateChanged() and subsequent
- * get{Voice/Data}RegistrationState().
- *
- * <p>Because registration failures are ephemeral, this callback is not sticky.
- * Registrants will not receive the most recent past value when registering.
- *
- * @param cellIdentity the CellIdentity, which must include the globally unique identifier
- * for the cell (for example, all components of the CGI or ECGI).
- * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
- * cell that was chosen for the failed registration attempt.
- * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
- * @param causeCode the primary failure cause code of the procedure.
- * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
- * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
- * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
- * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
- * Integer.MAX_VALUE if this value is unused.
- * @param additionalCauseCode the cause code of any secondary/combined procedure
- * if appropriate. For UMTS, if a combined attach succeeds for
- * PS only, then the GMM cause code shall be included as an
- * additionalCauseCode. For LTE (ESM), cause codes are in
- * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
- */
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PRECISE_PHONE_STATE,
- Manifest.permission.ACCESS_FINE_LOCATION
- })
- public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
- @NonNull String chosenPlmn, @Domain int domain,
- int causeCode, int additionalCauseCode);
- }
-
- /**
- * Interface for the current allowed network type list listener. This list involves values of
- * allowed network type for each of reasons.
- *
- * @hide
- */
- @SystemApi
- public interface AllowedNetworkTypesChangedListener {
- /**
- * Callback invoked when the current allowed network type list has changed on the
- * registered subscription.
- * Note, the registered subscription is associated with {@link TelephonyManager} object
- * on which
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
- * was called.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * given subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param allowedNetworkTypesList Map associating all allowed network type reasons
- * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
- * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
- * network type values.
- * For example:
- * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
- * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
- */
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- void onAllowedNetworkTypesChanged(
- @NonNull Map<Integer, Long> allowedNetworkTypesList);
- }
-
- /**
- * Interface for call attributes listener.
- *
- * @hide
- */
- @SystemApi
- public interface CallAttributesChangedListener {
- /**
- * Callback invoked when the call attributes changes on the registered subscription.
- * Note, the registration subscription ID comes from {@link TelephonyManager} object
- * which registers PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subscription ID. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param callAttributes the call attributes
- */
- @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
- }
-
- /**
- * Interface for barring information listener.
- */
- public interface BarringInfoChangedListener {
- /**
- * Report updated barring information for the current camped/registered cell.
- *
- * <p>Barring info is provided for all services applicable to the current camped/registered
- * cell, for the registered PLMN and current access class/access category.
- *
- * @param barringInfo for all services on the current cell.
- * @see android.telephony.BarringInfo
- */
- @RequiresPermission(allOf = {
- Manifest.permission.READ_PRECISE_PHONE_STATE,
- Manifest.permission.ACCESS_FINE_LOCATION
- })
- public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
- }
-
- /**
- * Interface for current physical channel configuration listener.
- * @hide
- */
- @SystemApi
- public interface PhysicalChannelConfigChangedListener {
- /**
- * Callback invoked when the current physical channel configuration has changed
- *
- * @param configs List of the current {@link PhysicalChannelConfig}s
- */
- @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
- }
-
- /**
- * Interface for data enabled listener.
- *
- * @hide
- */
- @SystemApi
- public interface DataEnabledChangedListener {
- /**
- * Callback invoked when the data enabled changes.
- *
- * @param enabled {@code true} if data is enabled, otherwise disabled.
- * @param reason Reason for data enabled/disabled.
- * See {@link TelephonyManager.DataEnabledReason}.
- */
- @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- public void onDataEnabledChanged(boolean enabled,
- @DataEnabledReason int reason);
+ mSubId = subId;
+ callback = new IPhoneStateListenerStub(this, e);
}
/**
@@ -1950,7 +609,9 @@ public class PhoneStateListener {
* @see ServiceState#STATE_IN_SERVICE
* @see ServiceState#STATE_OUT_OF_SERVICE
* @see ServiceState#STATE_POWER_OFF
+ * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
*/
+ @Deprecated
public void onServiceStateChanged(ServiceState serviceState) {
// default implementation empty
}
@@ -1983,7 +644,10 @@ public class PhoneStateListener {
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
*/
+ @Deprecated
public void onMessageWaitingIndicatorChanged(boolean mwi) {
// default implementation empty
}
@@ -1996,7 +660,10 @@ public class PhoneStateListener {
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
*/
+ @Deprecated
public void onCallForwardingIndicatorChanged(boolean cfi) {
// default implementation empty
}
@@ -2009,7 +676,10 @@ public class PhoneStateListener {
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
*/
+ @Deprecated
public void onCellLocationChanged(CellLocation location) {
// default implementation empty
}
@@ -2036,7 +706,10 @@ public class PhoneStateListener {
* {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
* privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
* passed as an argument.
+ *
+ * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
*/
+ @Deprecated
public void onCallStateChanged(@CallState int state, String phoneNumber) {
// default implementation empty
}
@@ -2054,14 +727,19 @@ public class PhoneStateListener {
* @see TelephonyManager#DATA_CONNECTING
* @see TelephonyManager#DATA_CONNECTED
* @see TelephonyManager#DATA_SUSPENDED
+ * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
*/
+ @Deprecated
public void onDataConnectionStateChanged(int state) {
// default implementation empty
}
/**
* same as above, but with the network type. Both called.
+ *
+ * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
*/
+ @Deprecated
public void onDataConnectionStateChanged(int state, int networkType) {
// default implementation empty
}
@@ -2080,7 +758,9 @@ public class PhoneStateListener {
* @see TelephonyManager#DATA_ACTIVITY_OUT
* @see TelephonyManager#DATA_ACTIVITY_INOUT
* @see TelephonyManager#DATA_ACTIVITY_DORMANT
+ * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
*/
+ @Deprecated
public void onDataActivity(int direction) {
// default implementation empty
}
@@ -2093,7 +773,10 @@ public class PhoneStateListener {
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
*/
+ @Deprecated
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
// default implementation empty
}
@@ -2109,7 +792,9 @@ public class PhoneStateListener {
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @param cellInfo is the list of currently visible cells.
+ * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
*/
+ @Deprecated
public void onCellInfoChanged(List<CellInfo> cellInfo) {
// default implementation empty
}
@@ -2122,11 +807,14 @@ public class PhoneStateListener {
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
* @param callState {@link PreciseCallState}
* @hide
+ * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@SystemApi
+ @Deprecated
public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
// default implementation empty
}
@@ -2142,9 +830,10 @@ public class PhoneStateListener {
*
* @param disconnectCause {@link DisconnectCause}.
* @param preciseDisconnectCause {@link PreciseDisconnectCause}.
- *
+ * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ @Deprecated
public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
@PreciseDisconnectCauses int preciseDisconnectCause) {
// default implementation empty
@@ -2160,9 +849,10 @@ public class PhoneStateListener {
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
- *
+ * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ @Deprecated
public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
// default implementation empty
}
@@ -2183,8 +873,10 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @param dataConnectionState {@link PreciseDataConnectionState}
+ * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @Deprecated
public void onPreciseDataConnectionStateChanged(
@NonNull PreciseDataConnectionState dataConnectionState) {
// default implementation empty
@@ -2200,8 +892,10 @@ public class PhoneStateListener {
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @hide
+ * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public void onDataConnectionRealTimeInfoChanged(
DataConnectionRealTimeInfo dcRtInfo) {
// default implementation empty
@@ -2219,11 +913,12 @@ public class PhoneStateListener {
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @hide
+ * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
*/
@SystemApi
+ @Deprecated
public void onSrvccStateChanged(@SrvccState int srvccState) {
// default implementation empty
-
}
/**
@@ -2238,8 +933,10 @@ public class PhoneStateListener {
*
* @param state is the current SIM voice activation state
* @hide
+ * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
*/
@SystemApi
+ @Deprecated
public void onVoiceActivationStateChanged(@SimActivationState int state) {
// default implementation empty
}
@@ -2256,7 +953,9 @@ public class PhoneStateListener {
*
* @param state is the current SIM data activation state
* @hide
+ * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
*/
+ @Deprecated
public void onDataActivationStateChanged(@SimActivationState int state) {
// default implementation empty
}
@@ -2271,7 +970,9 @@ public class PhoneStateListener {
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @param enabled indicates whether the current user mobile data state is enabled or disabled.
+ * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
*/
+ @Deprecated
public void onUserMobileDataStateChanged(boolean enabled) {
// default implementation empty
}
@@ -2285,8 +986,10 @@ public class PhoneStateListener {
* app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @param telephonyDisplayInfo The display information.
+ * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @Deprecated
public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
// default implementation empty
}
@@ -2309,7 +1012,9 @@ public class PhoneStateListener {
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
* the key and a list of emergency numbers as the value. If no
* emergency number information is available, the value will be null.
+ * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
*/
+ @Deprecated
public void onEmergencyNumberListChanged(
@NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
// default implementation empty
@@ -2322,7 +1027,6 @@ public class PhoneStateListener {
* the no-SIM case), regardless of which subscription this listener was registered on.
*
* @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
- *
* @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}.
* @hide
*/
@@ -2349,8 +1053,10 @@ public class PhoneStateListener {
* are no SIM cards in the device), this will be equal to
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
* @hide
+ * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
*/
@SystemApi
+ @Deprecated
public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
int subscriptionId) {
// Default implementation for backwards compatibility
@@ -2365,6 +1071,7 @@ public class PhoneStateListener {
*
* @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}.
* @hide
+ * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
*/
@SystemApi
@Deprecated
@@ -2386,8 +1093,10 @@ public class PhoneStateListener {
* @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
* @param subscriptionId The subscription ID used to send the emergency sms.
* @hide
+ * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
*/
@SystemApi
+ @Deprecated
public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
int subscriptionId) {
// Default implementation for backwards compatibility
@@ -2397,8 +1106,7 @@ public class PhoneStateListener {
/**
* Callback invoked when OEM hook raw event is received on the registered subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by
- * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
* If this TelephonyManager object was created with
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
@@ -2407,8 +1115,10 @@ public class PhoneStateListener {
* Requires the READ_PRIVILEGED_PHONE_STATE permission.
* @param rawData is the byte array of the OEM hook raw data.
* @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public void onOemHookRawEvent(byte[] rawData) {
// default implementation empty
}
@@ -2419,7 +1129,9 @@ public class PhoneStateListener {
*
* @param capability the new phone capability
* @hide
+ * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
*/
+ @Deprecated
public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) {
// default implementation empty
}
@@ -2432,7 +1144,9 @@ public class PhoneStateListener {
* @param subId current subscription used to setup Cellular Internet data.
* For example, it could be the current active opportunistic subscription in use,
* or the subscription user selected as default data subscription in DSDS mode.
+ * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
*/
+ @Deprecated
public void onActiveDataSubscriptionIdChanged(int subId) {
// default implementation empty
}
@@ -2449,8 +1163,10 @@ public class PhoneStateListener {
* Requires the READ_PRECISE_PHONE_STATE permission.
* @param callAttributes the call attributes
* @hide
+ * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
*/
@SystemApi
+ @Deprecated
public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
// default implementation empty
}
@@ -2468,8 +1184,10 @@ public class PhoneStateListener {
*
* @param state the modem radio power state
* @hide
+ * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
*/
@SystemApi
+ @Deprecated
public void onRadioPowerStateChanged(@RadioPowerState int state) {
// default implementation empty
}
@@ -2487,9 +1205,10 @@ public class PhoneStateListener {
* @param active Whether the carrier network change is or shortly
* will be active. This value is true to indicate
* showing alternative UI and false to stop.
- *
* @hide
+ * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
*/
+ @Deprecated
public void onCarrierNetworkChange(boolean active) {
// default implementation empty
}
@@ -2520,7 +1239,9 @@ public class PhoneStateListener {
* For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
* included as an additionalCauseCode. For LTE (ESM), cause codes are in
* TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+ * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
*/
+ @Deprecated
public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
int domain, int causeCode, int additionalCauseCode) {
// default implementation empty
@@ -2533,9 +1254,10 @@ public class PhoneStateListener {
* cell, for the registered PLMN and current access class/access category.
*
* @param barringInfo for all services on the current cell.
- *
* @see android.telephony.BarringInfo
+ * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
*/
+ @Deprecated
public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
// default implementation empty
}
@@ -2831,7 +1553,7 @@ public class PhoneStateListener {
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> psl.onRegistrationFailed(
- cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+ cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
// default implementation empty
}
@@ -2844,33 +1566,15 @@ public class PhoneStateListener {
}
public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
- PhysicalChannelConfigChangedListener listener =
- (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get();
- if (listener == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
- configs)));
+ // default implementation empty
}
public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) {
- DataEnabledChangedListener listener =
- (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get();
- if (listener == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
- enabled, reason)));
+ // default implementation empty
}
public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
- AllowedNetworkTypesChangedListener listener =
- (AllowedNetworkTypesChangedListener) mPhoneStateListenerWeakRef.get();
- if (listener == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+ // default implementation empty
}
}
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
new file mode 100644
index 000000000000..a2584cae1b9c
--- /dev/null
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -0,0 +1,1710 @@
+/*
+ * 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.telephony;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Binder;
+import android.os.Build;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IPhoneStateListener;
+
+import dalvik.system.VMRuntime;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * To register a callback, use a {@link TelephonyCallback} which implements interfaces regarding
+ * EVENT_*. For example,
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
+ * <p>
+ * Then override the methods for the state that you wish to receive updates for, and
+ * pass the executor and your TelephonyCallback object to
+ * {@link TelephonyManager#registerTelephonyCallback}.
+ * Methods are called when the state changes, as well as once on initial registration.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate sub-interfaces.
+ */
+public class TelephonyCallback {
+
+ /**
+ * Experiment flag to set the per-pid registration limit for TelephonyCallback
+ *
+ * Limit on registrations of {@link TelephonyCallback}s on a per-pid basis. When this limit is
+ * exceeded, any calls to {@link TelephonyManager#registerTelephonyCallback} will fail with an
+ * {@link IllegalStateException}.
+ *
+ * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
+ * TelephonyRegistry runs under are exempt from this limit.
+ *
+ * If the value of the flag is less than 1, enforcement of the limit will be disabled.
+ * @hide
+ */
+ public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
+ "phone_state_listener_per_pid_registration_limit";
+
+ /**
+ * Default value for the per-pid registration limit.
+ * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
+ * @hide
+ */
+ public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
+
+ /**
+ * This change enables a limit on the number of {@link TelephonyCallback} objects any process
+ * may register via {@link TelephonyManager#registerTelephonyCallback}. The default limit is 50,
+ * which may change via remote device config updates.
+ *
+ * This limit is enforced via an {@link IllegalStateException} thrown from
+ * {@link TelephonyManager#registerTelephonyCallback} when the offending process attempts to
+ * register one too many callbacks.
+ *
+ * @hide
+ */
+ @ChangeId
+ public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
+
+ /**
+ * Event for changes to the network service state (cellular).
+ *
+ * @hide
+ * @see ServiceStateListener#onServiceStateChanged
+ * @see ServiceState
+ */
+ @SystemApi
+ public static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
+ /**
+ * Event for changes to the network signal strength (cellular).
+ *
+ * @hide
+ * @see SignalStrengthsListener#onSignalStrengthsChanged
+ */
+ @SystemApi
+ public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
+
+ /**
+ * Event for changes to the message-waiting indicator.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+ * the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ * <p>
+ * Example: The status bar uses this to determine when to display the
+ * voicemail icon.
+ *
+ * @hide
+ * @see MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
+
+ /**
+ * Event for changes to the call-forwarding indicator.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+ * the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see CallForwardingIndicatorListener#onCallForwardingIndicatorChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
+
+ /**
+ * Event for changes to the device's cell location. Note that
+ * this will result in frequent listeners to the listener.
+ * <p>
+ * If you need regular location updates but want more control over
+ * the update interval or location precision, you can set up a callback
+ * through the {@link android.location.LocationManager location manager}
+ * instead.
+ *
+ * @hide
+ * @see CellLocationListener#onCellLocationChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public static final int EVENT_CELL_LOCATION_CHANGED = 5;
+
+ /**
+ * Event for changes to the device call state.
+ *
+ * @hide
+ * @see CallStateListener#onCallStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+ public static final int EVENT_CALL_STATE_CHANGED = 6;
+
+ /**
+ * Event for changes to the data connection state (cellular).
+ *
+ * @hide
+ * @see DataConnectionStateListener#onDataConnectionStateChanged
+ */
+ @SystemApi
+ public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
+
+ /**
+ * Event for changes to the direction of data traffic on the data
+ * connection (cellular).
+ * <p>
+ * Example: The status bar uses this to display the appropriate
+ * data-traffic icon.
+ *
+ * @hide
+ * @see DataActivityListener#onDataActivity
+ */
+ @SystemApi
+ public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
+
+ /**
+ * Event for changes to the network signal strengths (cellular).
+ * <p>
+ * Example: The status bar uses this to control the signal-strength
+ * icon.
+ *
+ * @hide
+ * @see SignalStrengthsListener#onSignalStrengthsChanged
+ */
+ @SystemApi
+ public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
+
+ /**
+ * Event for changes of the network signal strengths (cellular) always reported from modem,
+ * even in some situations such as the screen of the device is off.
+ *
+ * @hide
+ * @see AlwaysReportedSignalStrengthListener#onSignalStrengthsChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+ public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
+
+ /**
+ * Event for changes to observed cell info.
+ *
+ * @hide
+ * @see CellInfoListener#onCellInfoChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public static final int EVENT_CELL_INFO_CHANGED = 11;
+
+ /**
+ * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+ * background and foreground calls.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see PreciseCallStateListener#onPreciseCallStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
+
+ /**
+ * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
+
+ /**
+ * Event for real time info for all data connections (cellular)).
+ *
+ * @hide
+ * @see PhoneStateListener#onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+ * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
+
+ /**
+ * Event for OEM hook raw event
+ *
+ * @hide
+ * @see PhoneStateListener#onOemHookRawEvent
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_OEM_HOOK_RAW = 15;
+
+ /**
+ * Event for changes to the SRVCC state of the active call.
+ *
+ * @hide
+ * @see SrvccStateListener#onSrvccStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_SRVCC_STATE_CHANGED = 16;
+
+ /**
+ * Event for carrier network changes indicated by a carrier app.
+ *
+ * @hide
+ * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+ * @see CarrierNetworkListener#onCarrierNetworkChange
+ */
+ @SystemApi
+ public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
+
+ /**
+ * Event for changes to the sim voice activation state
+ *
+ * @hide
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * <p>
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
+ * @see VoiceActivationStateListener#onVoiceActivationStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
+
+ /**
+ * Event for changes to the sim data activation state
+ *
+ * @hide
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * <p>
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
+ * @see DataActivationStateListener#onDataActivationStateChanged
+ */
+ @SystemApi
+ public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
+
+ /**
+ * Event for changes to the user mobile data state
+ *
+ * @hide
+ * @see UserMobileDataStateListener#onUserMobileDataStateChanged
+ */
+ @SystemApi
+ public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
+
+ /**
+ * Event for display info changed event.
+ *
+ * @hide
+ * @see DisplayInfoListener#onDisplayInfoChanged
+ */
+ @SystemApi
+ public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
+
+ /**
+ * Event for changes to the phone capability.
+ *
+ * @hide
+ * @see PhoneCapabilityListener#onPhoneCapabilityChanged
+ */
+ @SystemApi
+ public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
+
+ /**
+ * Event for changes to active data subscription ID. Active data subscription is
+ * the current subscription used to setup Cellular Internet data. For example,
+ * it could be the current active opportunistic subscription in use, or the
+ * subscription user selected as default data subscription in DSDS mode.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
+
+ /**
+ * Event for changes to the radio power state.
+ *
+ * @hide
+ * @see RadioPowerStateListener#onRadioPowerStateChanged
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
+
+ /**
+ * Event for changes to emergency number list based on all active subscriptions.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see EmergencyNumberListListener#onEmergencyNumberListChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
+
+ /**
+ * Event for call disconnect causes which contains {@link DisconnectCause} and
+ * {@link PreciseDisconnectCause}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see CallDisconnectCauseListener#onCallDisconnectCauseChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
+
+ /**
+ * Event for changes to the call attributes of a currently active call.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see CallAttributesListener#onCallAttributesChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
+
+ /**
+ * Event for IMS call disconnect causes which contains
+ * {@link android.telephony.ims.ImsReasonInfo}
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
+
+ /**
+ * Event for the emergency number placed from an outgoing call.
+ *
+ * @hide
+ * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
+
+ /**
+ * Event for the emergency number placed from an outgoing SMS.
+ *
+ * @hide
+ * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
+
+ /**
+ * Event for registration failures.
+ * <p>
+ * Event for indications that a registration procedure has failed in either the CS or PS
+ * domain. This indication does not necessarily indicate a change of service state, which should
+ * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+ * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
+ * @hide
+ * @see RegistrationFailedListener#onRegistrationFailed
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public static final int EVENT_REGISTRATION_FAILURE = 31;
+
+ /**
+ * Event for Barring Information for the current registered / camped cell.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+ * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
+ * @hide
+ * @see BarringInfoListener#onBarringInfoChanged
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public static final int EVENT_BARRING_INFO_CHANGED = 32;
+
+ /**
+ * Event for changes to the physical channel configuration.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see PhysicalChannelConfigListener#onPhysicalChannelConfigChanged
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
+
+
+ /**
+ * Event for changes to the data enabled.
+ * <p>
+ * Event for indications that the enabled status of current data has changed.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see DataEnabledListener#onDataEnabledChanged
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_DATA_ENABLED_CHANGED = 34;
+
+ /**
+ * Event for changes to allowed network list based on all active subscriptions.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @hide
+ * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"EVENT_"}, value = {
+ EVENT_SERVICE_STATE_CHANGED,
+ EVENT_SIGNAL_STRENGTH_CHANGED,
+ EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
+ EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
+ EVENT_CELL_LOCATION_CHANGED,
+ EVENT_CALL_STATE_CHANGED,
+ EVENT_DATA_CONNECTION_STATE_CHANGED,
+ EVENT_DATA_ACTIVITY_CHANGED,
+ EVENT_SIGNAL_STRENGTHS_CHANGED,
+ EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
+ EVENT_CELL_INFO_CHANGED,
+ EVENT_PRECISE_CALL_STATE_CHANGED,
+ EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
+ EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
+ EVENT_OEM_HOOK_RAW,
+ EVENT_SRVCC_STATE_CHANGED,
+ EVENT_CARRIER_NETWORK_CHANGED,
+ EVENT_VOICE_ACTIVATION_STATE_CHANGED,
+ EVENT_DATA_ACTIVATION_STATE_CHANGED,
+ EVENT_USER_MOBILE_DATA_STATE_CHANGED,
+ EVENT_DISPLAY_INFO_CHANGED,
+ EVENT_PHONE_CAPABILITY_CHANGED,
+ EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
+ EVENT_RADIO_POWER_STATE_CHANGED,
+ EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
+ EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
+ EVENT_CALL_ATTRIBUTES_CHANGED,
+ EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
+ EVENT_OUTGOING_EMERGENCY_CALL,
+ EVENT_OUTGOING_EMERGENCY_SMS,
+ EVENT_REGISTRATION_FAILURE,
+ EVENT_BARRING_INFO_CHANGED,
+ EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
+ EVENT_DATA_ENABLED_CHANGED,
+ EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TelephonyEvent {
+ }
+
+ /**
+ * @hide
+ */
+ //TODO: The maxTargetSdk should be S if the build time tool updates it.
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public IPhoneStateListener callback;
+
+ /**
+ * @hide
+ */
+ public void init(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("TelephonyCallback Executor must be non-null");
+ }
+ callback = new IPhoneStateListenerStub(this, executor);
+ }
+
+ /**
+ * Interface for service state listener.
+ */
+ public interface ServiceStateListener {
+ /**
+ * Callback invoked when device service state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * <p>
+ * The instance of {@link ServiceState} passed as an argument here will have various
+ * levels of location information stripped from it depending on the location permissions
+ * that your app holds.
+ * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+ * receive all the information in {@link ServiceState}.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onServiceStateChanged(@NonNull ServiceState serviceState);
+ }
+
+ /**
+ * Interface for message waiting indicator listener.
+ */
+ public interface MessageWaitingIndicatorListener {
+ /**
+ * Callback invoked when the message-waiting indicator changes on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onMessageWaitingIndicatorChanged(boolean mwi);
+ }
+
+ /**
+ * Interface for call-forwarding indicator listener.
+ */
+ public interface CallForwardingIndicatorListener {
+ /**
+ * Callback invoked when the call-forwarding indicator changes on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onCallForwardingIndicatorChanged(boolean cfi);
+ }
+
+ /**
+ * Interface for device cell location listener.
+ */
+ public interface CellLocationListener {
+ /**
+ * Callback invoked when device cell location changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public void onCellLocationChanged(@NonNull CellLocation location);
+ }
+
+ /**
+ * Interface for call state listener.
+ */
+ public interface CallStateListener {
+ /**
+ * Callback invoked when device call state changes.
+ * <p>
+ * Reports the state of Telephony (mobile) calls on the device for the registered
+ * subscription.
+ * <p>
+ * Note: the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * <p>
+ * Note: The state returned here may differ from that returned by
+ * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+ * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+ * different state than the callback reports.
+ *
+ * @param state call state
+ * @param phoneNumber call phone number. If application does not have
+ * {@link android.Manifest.permission#READ_CALL_LOG} permission or
+ * carrier
+ * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an
+ * empty string will be
+ * passed as an argument.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+ public void onCallStateChanged(@Annotation.CallState int state,
+ @Nullable String phoneNumber);
+ }
+
+ /**
+ * Interface for data connection state listener.
+ */
+ public interface DataConnectionStateListener {
+ /**
+ * Callback invoked when connection state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current state of data connection.
+ * @param networkType is the current network type of data connection.
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
+ @Annotation.NetworkType int networkType);
+ }
+
+ /**
+ * Interface for data activity state listener.
+ */
+ public interface DataActivityListener {
+ /**
+ * Callback invoked when data activity state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see TelephonyManager#DATA_ACTIVITY_NONE
+ * @see TelephonyManager#DATA_ACTIVITY_IN
+ * @see TelephonyManager#DATA_ACTIVITY_OUT
+ * @see TelephonyManager#DATA_ACTIVITY_INOUT
+ * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataActivity(@Annotation.DataActivityType int direction);
+ }
+
+ /**
+ * Interface for network signal strengths listener.
+ */
+ public interface SignalStrengthsListener {
+ /**
+ * Callback invoked when network signal strengths changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+ }
+
+ /**
+ * Interface for network signal strengths callback which always reported from modem.
+ */
+ public interface AlwaysReportedSignalStrengthListener {
+ /**
+ * Callback always invoked from modem when network signal strengths changes on the
+ * registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+ }
+
+ /**
+ * Interface for cell info listener.
+ */
+ public interface CellInfoListener {
+ /**
+ * Callback invoked when a observed cell info has changed or new cells have been added
+ * or removed on the registered subscription.
+ * Note, the registration subscription ID s from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param cellInfo is the list of currently visible cells.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+ }
+
+ /**
+ * Interface for precise device call state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface PreciseCallStateListener {
+ /**
+ * Callback invoked when precise device call state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param callState {@link PreciseCallState}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+ }
+
+ /**
+ * Interface for call disconnect cause listener.
+ */
+ public interface CallDisconnectCauseListener {
+ /**
+ * Callback invoked when call disconnect cause changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param disconnectCause {@link DisconnectCause}.
+ * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+ @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+ }
+
+ /**
+ * Interface for IMS call disconnect cause listener.
+ */
+ public interface ImsCallDisconnectCauseListener {
+ /**
+ * Callback invoked when IMS call disconnect cause changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+ }
+
+ /**
+ * Interface for precise data connection state listener.
+ */
+ public interface PreciseDataConnectionStateListener {
+ /**
+ * Callback providing update about the default/internet data connection on the registered
+ * subscription.
+ * <p>
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param dataConnectionState {@link PreciseDataConnectionState}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPreciseDataConnectionStateChanged(
+ @NonNull PreciseDataConnectionState dataConnectionState);
+ }
+
+ /**
+ * Interface for Single Radio Voice Call Continuity listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface SrvccStateListener {
+ /**
+ * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+ * (SRVCC) state for the currently active call on the registered subscription.
+ * <p>
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
+ }
+
+ /**
+ * Interface for SIM voice activation state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface VoiceActivationStateListener {
+ /**
+ * Callback invoked when the SIM voice activation state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM voice activation state
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
+
+ }
+
+ /**
+ * Interface for SIM data activation state listener.
+ */
+ public interface DataActivationStateListener {
+ /**
+ * Callback invoked when the SIM data activation state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM data activation state
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataActivationStateChanged(@Annotation.SimActivationState int state);
+ }
+
+ /**
+ * Interface for user mobile data state listener.
+ */
+ public interface UserMobileDataStateListener {
+ /**
+ * Callback invoked when the user mobile data state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param enabled indicates whether the current user mobile data state is enabled or
+ * disabled.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onUserMobileDataStateChanged(boolean enabled);
+ }
+
+ /**
+ * Interface for display info listener.
+ */
+ public interface DisplayInfoListener {
+ /**
+ * Callback invoked when the display info has changed on the registered subscription.
+ * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
+ * based on carrier policy.
+ *
+ * @param telephonyDisplayInfo The display information.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+ }
+
+ /**
+ * Interface for the current emergency number list listener.
+ */
+ public interface EmergencyNumberListListener {
+ /**
+ * Callback invoked when the current emergency number list has changed on the registered
+ * subscription.
+ * <p>
+ * Note, the registered subscription is associated with {@link TelephonyManager} object
+ * on which
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+ * was called.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * given subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param emergencyNumberList Map associating all active subscriptions on the device with
+ * the list of emergency numbers originating from that
+ * subscription.
+ * If there are no active subscriptions, the map will contain a
+ * single entry with
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+ * the key and a list of emergency numbers as the value. If no
+ * emergency number information is available, the value will be
+ * empty.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onEmergencyNumberListChanged(
+ @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
+ }
+
+ /**
+ * Interface for outgoing emergency call listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OutgoingEmergencyCallListener {
+ /**
+ * Callback invoked when an outgoing call is placed to an emergency number.
+ * <p>
+ * This method will be called when an emergency call is placed on any subscription
+ * (including the no-SIM case), regardless of which subscription this callback was
+ * registered on.
+ * <p>
+ *
+ * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
+ * placed to.
+ * @param subscriptionId The subscription ID used to place the emergency call. If the
+ * emergency call was placed without a valid subscription
+ * (e.g. when there are no SIM cards in the device), this
+ * will be
+ * equal to
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ */
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+ int subscriptionId);
+ }
+
+ /**
+ * Interface for outgoing emergency sms listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OutgoingEmergencySmsListener {
+ /**
+ * Smsback invoked when an outgoing sms is sent to an emergency number.
+ * <p>
+ * This method will be called when an emergency sms is sent on any subscription,
+ * regardless of which subscription this callback was registered on.
+ *
+ * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+ * @param subscriptionId The subscription ID used to send the emergency sms.
+ */
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId);
+ }
+
+ /**
+ * Interface for phone capability listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface PhoneCapabilityListener {
+ /**
+ * Callback invoked when phone capability changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * @param capability the new phone capability
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+ }
+
+ /**
+ * Interface for active data subscription ID listener.
+ */
+ public interface ActiveDataSubscriptionIdListener {
+ /**
+ * Callback invoked when active data subscription ID changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * @param subId current subscription used to setup Cellular Internet data.
+ * For example, it could be the current active opportunistic subscription
+ * in use, or the subscription user selected as default data subscription in
+ * DSDS mode.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onActiveDataSubscriptionIdChanged(int subId);
+ }
+
+ /**
+ * Interface for modem radio power state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface RadioPowerStateListener {
+ /**
+ * Callback invoked when modem radio power state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state the modem radio power state
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
+ }
+
+ /**
+ * Interface for carrier network listener.
+ */
+ public interface CarrierNetworkListener {
+ /**
+ * Callback invoked when telephony has received notice from a carrier
+ * app that a network action that could result in connectivity loss
+ * has been requested by an app using
+ * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
+ * <p>
+ * This is optional and is only used to allow the system to provide alternative UI while
+ * telephony is performing an action that may result in intentional, temporary network
+ * lack of connectivity.
+ * <p>
+ * Note, this callback is pinned to the registered subscription and will be invoked when
+ * the notifying carrier app has carrier privilege rule on the registered
+ * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+ *
+ * @param active If the carrier network change is or shortly will be active,
+ * {@code true} indicate that showing alternative UI, {@code false} otherwise.
+ */
+ public void onCarrierNetworkChange(boolean active);
+ }
+
+ /**
+ * Interface for registration failures listener.
+ */
+ public interface RegistrationFailedListener {
+ /**
+ * Report that Registration or a Location/Routing/Tracking Area update has failed.
+ *
+ * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+ * area update fails. This includes procedures that do not necessarily result in a change of
+ * the modem's registration status. If the modem's registration status changes, that is
+ * reflected in the onNetworkStateChanged() and subsequent
+ * get{Voice/Data}RegistrationState().
+ *
+ * <p>Because registration failures are ephemeral, this callback is not sticky.
+ * Registrants will not receive the most recent past value when registering.
+ *
+ * @param cellIdentity the CellIdentity, which must include the globally unique
+ * identifier
+ * for the cell (for example, all components of the CGI or ECGI).
+ * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those
+ * broadcast by the
+ * cell that was chosen for the failed registration attempt.
+ * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+ * @param causeCode the primary failure cause code of the procedure.
+ * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+ * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+ * Integer.MAX_VALUE if this value is unused.
+ * @param additionalCauseCode the cause code of any secondary/combined procedure
+ * if appropriate. For UMTS, if a combined attach succeeds for
+ * PS only, then the GMM cause code shall be included as an
+ * additionalCauseCode. For LTE (ESM), cause codes are in
+ * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+ */
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+ @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode,
+ int additionalCauseCode);
+ }
+
+ /**
+ * Interface for the current allowed network type list listener. This list involves values of
+ * allowed network type for each of reasons.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface AllowedNetworkTypesListener {
+ /**
+ * Callback invoked when the current allowed network type list has changed on the
+ * registered subscription.
+ * Note, the registered subscription is associated with {@link TelephonyManager} object
+ * on which
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+ * was called.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * given subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param allowedNetworkTypesList Map associating all allowed network type reasons
+ * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
+ * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
+ * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
+ * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
+ * network type values.
+ * For example:
+ * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
+ * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
+ * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
+ * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+ }
+
+ /**
+ * Interface for call attributes listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CallAttributesListener {
+ /**
+ * Callback invoked when the call attributes changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers TelephonyCallback by
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param callAttributes the call attributes
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
+ }
+
+ /**
+ * Interface for barring information listener.
+ */
+ public interface BarringInfoListener {
+ /**
+ * Report updated barring information for the current camped/registered cell.
+ *
+ * <p>Barring info is provided for all services applicable to the current camped/registered
+ * cell, for the registered PLMN and current access class/access category.
+ *
+ * @param barringInfo for all services on the current cell.
+ * @see android.telephony.BarringInfo
+ */
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+ }
+
+ /**
+ * Interface for current physical channel configuration listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface PhysicalChannelConfigListener {
+ /**
+ * Callback invoked when the current physical channel configuration has changed
+ *
+ * @param configs List of the current {@link PhysicalChannelConfig}s
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+ }
+
+ /**
+ * Interface for data enabled listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface DataEnabledListener {
+ /**
+ * Callback invoked when the data enabled changes.
+ *
+ * @param enabled {@code true} if data is enabled, otherwise disabled.
+ * @param reason Reason for data enabled/disabled.
+ * See {@link TelephonyManager.DataEnabledReason}.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onDataEnabledChanged(boolean enabled,
+ @TelephonyManager.DataEnabledReason int reason);
+ }
+
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ * <p>
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IPhoneState.Stub callback retaining references to the outside TelephonyCallback:
+ * even caller has been destroyed and "un-registered" the TelephonyCallback, it is still not
+ * eligible for GC given the references coming from:
+ * Native Stack --> TelephonyCallback --> Context (Activity).
+ * memory of caller's context will be collected after GC from service side get triggered
+ */
+ private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+ private WeakReference<TelephonyCallback> mTelephonyCallbackWeakRef;
+ private Executor mExecutor;
+
+ IPhoneStateListenerStub(TelephonyCallback telephonyCallback, Executor executor) {
+ mTelephonyCallbackWeakRef = new WeakReference<TelephonyCallback>(telephonyCallback);
+ mExecutor = executor;
+ }
+
+ public void onServiceStateChanged(ServiceState serviceState) {
+ ServiceStateListener listener = (ServiceStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onServiceStateChanged(serviceState)));
+ }
+
+ public void onSignalStrengthChanged(int asu) {
+ // default implementation empty
+ }
+
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ MessageWaitingIndicatorListener listener =
+ (MessageWaitingIndicatorListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onMessageWaitingIndicatorChanged(mwi)));
+ }
+
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ CallForwardingIndicatorListener listener =
+ (CallForwardingIndicatorListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCallForwardingIndicatorChanged(cfi)));
+ }
+
+ public void onCellLocationChanged(CellIdentity cellIdentity) {
+ // There is no system/public API to create an CellIdentity in system server,
+ // so the server pass a null to indicate an empty initial location.
+ CellLocation location =
+ cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation();
+ CellLocationListener listener = (CellLocationListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCellLocationChanged(location)));
+ }
+
+ public void onCallStateChanged(int state, String incomingNumber) {
+ CallStateListener listener = (CallStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCallStateChanged(state,
+ incomingNumber)));
+ }
+
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ DataConnectionStateListener listener =
+ (DataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ if (state == TelephonyManager.DATA_DISCONNECTING
+ && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() ->
+ listener.onDataConnectionStateChanged(
+ TelephonyManager.DATA_CONNECTED, networkType)));
+ } else {
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() ->
+ listener.onDataConnectionStateChanged(state, networkType)));
+ }
+ }
+
+ public void onDataActivity(int direction) {
+ DataActivityListener listener = (DataActivityListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onDataActivity(direction)));
+ }
+
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ SignalStrengthsListener listener =
+ (SignalStrengthsListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onSignalStrengthsChanged(
+ signalStrength)));
+ }
+
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ CellInfoListener listener = (CellInfoListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCellInfoChanged(cellInfo)));
+ }
+
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ PreciseCallStateListener listener =
+ (PreciseCallStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onPreciseCallStateChanged(callState)));
+ }
+
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ CallDisconnectCauseListener listener =
+ (CallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCallDisconnectCauseChanged(
+ disconnectCause, preciseDisconnectCause)));
+ }
+
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ PreciseDataConnectionStateListener listener =
+ (PreciseDataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onPreciseDataConnectionStateChanged(
+ dataConnectionState)));
+ }
+
+ public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+ // default implementation empty
+ }
+
+ public void onSrvccStateChanged(int state) {
+ SrvccStateListener listener = (SrvccStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onSrvccStateChanged(state)));
+ }
+
+ public void onVoiceActivationStateChanged(int activationState) {
+ VoiceActivationStateListener listener =
+ (VoiceActivationStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onVoiceActivationStateChanged(activationState)));
+ }
+
+ public void onDataActivationStateChanged(int activationState) {
+ DataActivationStateListener listener =
+ (DataActivationStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onDataActivationStateChanged(activationState)));
+ }
+
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ UserMobileDataStateListener listener =
+ (UserMobileDataStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onUserMobileDataStateChanged(enabled)));
+ }
+
+ public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+ DisplayInfoListener listener = (DisplayInfoListener)mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onDisplayInfoChanged(telephonyDisplayInfo)));
+ }
+
+ public void onOemHookRawEvent(byte[] rawData) {
+ // default implementation empty
+ }
+
+ public void onCarrierNetworkChange(boolean active) {
+ CarrierNetworkListener listener =
+ (CarrierNetworkListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCarrierNetworkChange(active)));
+ }
+
+ public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+ EmergencyNumberListListener listener =
+ (EmergencyNumberListListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onEmergencyNumberListChanged(emergencyNumberList)));
+ }
+
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+ int subscriptionId) {
+ OutgoingEmergencyCallListener listener =
+ (OutgoingEmergencyCallListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onOutgoingEmergencyCall(placedEmergencyNumber,
+ subscriptionId)));
+ }
+
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId) {
+ OutgoingEmergencySmsListener listener =
+ (OutgoingEmergencySmsListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onOutgoingEmergencySms(sentEmergencyNumber,
+ subscriptionId)));
+ }
+
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ PhoneCapabilityListener listener =
+ (PhoneCapabilityListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onPhoneCapabilityChanged(capability)));
+ }
+
+ public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state) {
+ RadioPowerStateListener listener =
+ (RadioPowerStateListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onRadioPowerStateChanged(state)));
+ }
+
+ public void onCallAttributesChanged(CallAttributes callAttributes) {
+ CallAttributesListener listener =
+ (CallAttributesListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onCallAttributesChanged(
+ callAttributes)));
+ }
+
+ public void onActiveDataSubIdChanged(int subId) {
+ ActiveDataSubscriptionIdListener listener =
+ (ActiveDataSubscriptionIdListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onActiveDataSubscriptionIdChanged(
+ subId)));
+ }
+
+ public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
+ ImsCallDisconnectCauseListener listener =
+ (ImsCallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onImsCallDisconnectCauseChanged(disconnectCause)));
+ }
+
+ public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+ @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+ RegistrationFailedListener listener =
+ (RegistrationFailedListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onRegistrationFailed(
+ cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+ // default implementation empty
+ }
+
+ public void onBarringInfoChanged(BarringInfo barringInfo) {
+ BarringInfoListener listener = (BarringInfoListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onBarringInfoChanged(barringInfo)));
+ }
+
+ public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+ PhysicalChannelConfigListener listener =
+ (PhysicalChannelConfigListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
+ configs)));
+ }
+
+ public void onDataEnabledChanged(boolean enabled,
+ @TelephonyManager.DataEnabledReason int reason) {
+ DataEnabledListener listener =
+ (DataEnabledListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
+ enabled, reason)));
+ }
+
+ public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+ AllowedNetworkTypesListener listener =
+ (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+ }
+ }
+}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 3c355d4b6f45..459c6e94e4ac 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -209,7 +209,7 @@ public class TelephonyRegistryManager {
}
/**
- * To check the SDK version for {@link #listenWithEventList}.
+ * To check the SDK version for {@link #listenFromListener}.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
@@ -221,24 +221,49 @@ public class TelephonyRegistryManager {
* @param pkg Package name
* @param featureId Feature ID
* @param listener Listener providing callback
- * @param events List events
+ * @param events Events
* @param notifyNow Whether to notify instantly
*/
- public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId,
- @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) {
+ public void listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId,
+ @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow) {
+ if (listener == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+
try {
+ int[] eventsList = getEventsFromBitmask(events).stream().mapToInt(i -> i).toArray();
// subId from PhoneStateListener is deprecated Q on forward, use the subId from
// TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
// Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
// the only place to set mSubId and its for "informational" only.
- listener.mSubId = (events.length == 0)
+ listener.mSubId = (eventsList.length == 0)
? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
} else if (listener.mSubId != null) {
subId = listener.mSubId;
}
sRegistry.listenWithEventList(
- subId, pkg, featureId, listener.callback, events, notifyNow);
+ subId, pkg, featureId, listener.callback, eventsList, notifyNow);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Listen for incoming subscriptions
+ * @param subId Subscription ID
+ * @param pkg Package name
+ * @param featureId Feature ID
+ * @param telephonyCallback Listener providing callback
+ * @param events List events
+ * @param notifyNow Whether to notify instantly
+ */
+ private void listenFromCallback(int subId, @NonNull String pkg, @NonNull String featureId,
+ @NonNull TelephonyCallback telephonyCallback, @NonNull int[] events,
+ boolean notifyNow) {
+ try {
+ sRegistry.listenWithEventList(
+ subId, pkg, featureId, telephonyCallback.callback, events, notifyNow);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -815,136 +840,137 @@ public class TelephonyRegistryManager {
}
}
- public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) {
+ public @NonNull Set<Integer> getEventsFromCallback(
+ @NonNull TelephonyCallback telephonyCallback) {
Set<Integer> eventList = new ArraySet<>();
- if (listener instanceof PhoneStateListener.ServiceStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) {
+ eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) {
- eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.MessageWaitingIndicatorListener) {
+ eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
}
- if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CallForwardingIndicatorListener) {
+ eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
}
- if (listener instanceof PhoneStateListener.CellLocationChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CellLocationListener) {
+ eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
}
- if (listener instanceof PhoneStateListener.CallStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CallStateListener) {
+ eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.DataConnectionStateListener) {
+ eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.DataActivityListener) {
- eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.DataActivityListener) {
+ eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
}
- if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) {
- eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.SignalStrengthsListener) {
+ eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
}
- if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) {
- eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.AlwaysReportedSignalStrengthListener) {
+ eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
}
- if (listener instanceof PhoneStateListener.CellInfoChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) {
+ eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
}
- if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.PreciseCallStateListener) {
+ eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CallDisconnectCauseListener) {
+ eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
}
- if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) {
- eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.ImsCallDisconnectCauseListener) {
+ eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
}
- if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.PreciseDataConnectionStateListener) {
+ eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.SrvccStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.SrvccStateListener) {
+ eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.VoiceActivationStateListener) {
+ eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.DataActivationStateListener) {
+ eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.UserMobileDataStateListener) {
+ eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) {
- eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.DisplayInfoListener) {
+ eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
}
- if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) {
- eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.EmergencyNumberListListener) {
+ eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
}
- if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) {
- eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+ if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencyCallListener) {
+ eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
}
- if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) {
- eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencySmsListener) {
+ eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
}
- if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) {
- eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.PhoneCapabilityListener) {
+ eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
}
- if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) {
- eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.ActiveDataSubscriptionIdListener) {
+ eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
}
- if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) {
- eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.RadioPowerStateListener) {
+ eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
}
- if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) {
- eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CarrierNetworkListener) {
+ eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
}
- if (listener instanceof PhoneStateListener.RegistrationFailedListener) {
- eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+ if (telephonyCallback instanceof TelephonyCallback.RegistrationFailedListener) {
+ eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
}
- if (listener instanceof PhoneStateListener.CallAttributesChangedListener) {
- eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.CallAttributesListener) {
+ eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
}
- if (listener instanceof PhoneStateListener.BarringInfoChangedListener) {
- eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.BarringInfoListener) {
+ eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
}
- if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) {
- eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.PhysicalChannelConfigListener) {
+ eventList.add(TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
}
- if (listener instanceof PhoneStateListener.DataEnabledChangedListener) {
- eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.DataEnabledListener) {
+ eventList.add(TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
}
- if (listener instanceof PhoneStateListener.AllowedNetworkTypesChangedListener) {
- eventList.add(PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+ if (telephonyCallback instanceof TelephonyCallback.AllowedNetworkTypesListener) {
+ eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
}
return eventList;
@@ -955,200 +981,183 @@ public class TelephonyRegistryManager {
Set<Integer> eventList = new ArraySet<>();
if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
- eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
- eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
- eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
- eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
- eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
- eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
- eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
- eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
- eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
- eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW);
+ eventList.add(TelephonyCallback.EVENT_OEM_HOOK_RAW);
}
if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
- eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
- eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
- eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
- eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
- eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
- eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
- eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
- eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
- eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
- eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
- eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
}
if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) {
- eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+ eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
}
if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) {
- eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
}
if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) {
- eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+ eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
}
if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
- eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
}
return eventList;
}
/**
- * Registers a listener object to receive notification of changes
- * in specified telephony states.
+ * Registers a callback object to receive notification of changes in specified telephony states.
* <p>
- * To register a listener, pass a {@link PhoneStateListener} which implements
+ * To register a callback, pass a {@link TelephonyCallback} which implements
* interfaces of events. For example,
- * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
- * {@link PhoneStateListener.ServiceStateChangedListener}.
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
*
* At registration, and when a specified telephony state changes, the telephony manager invokes
- * the appropriate callback method on the listener object and passes the current (updated)
+ * the appropriate callback method on the callback object and passes the current (updated)
* values.
* <p>
*
* If this TelephonyManager object has been created with
* {@link TelephonyManager#createForSubscriptionId}, applies to the given subId.
* Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
- * To listen events for multiple subIds, pass a separate listener object to
+ * To register events for multiple subIds, pass a separate callback object to
* each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}.
*
* Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
*
- * This API should be used sparingly -- large numbers of listeners will cause system
- * instability. If a process has registered too many listeners without unregistering them, it
- * may encounter an {@link IllegalStateException} when trying to register more listeners.
+ * This API should be used sparingly -- large numbers of callbacks will cause system
+ * instability. If a process has registered too many callbacks without unregistering them, it
+ * may encounter an {@link IllegalStateException} when trying to register more callbacks.
*
- * @param listener The {@link PhoneStateListener} object to register.
+ * @param callback The {@link TelephonyCallback} object to register.
*/
- public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId,
- String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
+ public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+ int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback,
boolean notifyNow) {
- listener.setExecutor(executor);
- registerPhoneStateListener(subId, pkgName, attributionTag, listener,
- getEventsFromListener(listener), notifyNow);
- }
-
- public void registerPhoneStateListenerWithEvents(int subId, String pkgName,
- String attributionTag, @NonNull PhoneStateListener listener, int events,
- boolean notifyNow) {
- registerPhoneStateListener(
- subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow);
- }
-
- private void registerPhoneStateListener(int subId,
- String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
- @NonNull Set<Integer> events, boolean notifyNow) {
- if (listener == null) {
+ if (callback == null) {
throw new IllegalStateException("telephony service is null.");
}
-
- listenWithEventList(subId, pkgName, attributionTag, listener,
- events.stream().mapToInt(i -> i).toArray(), notifyNow);
+ callback.init(executor);
+ listenFromCallback(subId, pkgName, attributionTag, callback,
+ getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow);
}
/**
- * Unregister an existing {@link PhoneStateListener}.
+ * Unregister an existing {@link TelephonyCallback}.
*
- * @param listener The {@link PhoneStateListener} object to unregister.
+ * @param callback The {@link TelephonyCallback} object to unregister.
*/
- public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag,
- @NonNull PhoneStateListener listener,
- boolean notifyNow) {
- listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow);
+ public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag,
+ @NonNull TelephonyCallback callback, boolean notifyNow) {
+ listenFromCallback(subId, pkgName, attributionTag, callback, new int[0], notifyNow);
}
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2b577d04b18d..3a3302475069 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -68,7 +68,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "false");
+ DEFAULT_FLAGS.put("settings_silky_home", "true");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false");
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 14aa38682d2b..5425c214de1f 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -2,5 +2,7 @@ per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
-per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
+per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
+
+per-file PackageUtils.java = file:/core/java/android/content/pm/OWNERS
diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java
index 2edc4a7ff588..c77a02434941 100644
--- a/core/java/android/util/SizeF.java
+++ b/core/java/android/util/SizeF.java
@@ -16,8 +16,12 @@
package android.util;
-import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkArgumentFinite;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* Immutable class for describing width and height dimensions in some arbitrary
@@ -26,7 +30,7 @@ import static com.android.internal.util.Preconditions.checkArgumentFinite;
* Width and height are finite values stored as a floating point representation.
* </p>
*/
-public final class SizeF {
+public final class SizeF implements Parcelable {
/**
* Create a new immutable SizeF instance.
*
@@ -161,4 +165,43 @@ public final class SizeF {
private final float mWidth;
private final float mHeight;
+
+ /**
+ * Parcelable interface methods
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Write this size to the specified parcel. To restore a size from a parcel, use the
+ * {@link #CREATOR}.
+ * @param out The parcel to write the point's coordinates into
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeFloat(mWidth);
+ out.writeFloat(mHeight);
+ }
+
+ public static final @NonNull Creator<SizeF> CREATOR = new Creator<SizeF>() {
+ /**
+ * Return a new size from the data in the specified parcel.
+ */
+ @Override
+ public @NonNull SizeF createFromParcel(@NonNull Parcel in) {
+ float width = in.readFloat();
+ float height = in.readFloat();
+ return new SizeF(width, height);
+ }
+
+ /**
+ * Return an array of sizes of the specified size.
+ */
+ @Override
+ public @NonNull SizeF[] newArray(int size) {
+ return new SizeF[size];
+ }
+ };
}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 6718e93f908c..05c8617294da 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -510,10 +510,12 @@ public class SparseArray<E> implements Cloneable {
}
/**
+ * Compares the contents of this {@link SparseArray} to the specified {@link SparseArray}.
+ *
* For backwards compatibility reasons, {@link Object#equals(Object)} cannot be implemented,
* so this serves as a manually invoked alternative.
*/
- public boolean contentEquals(@Nullable SparseArray<E> other) {
+ public boolean contentEquals(@Nullable SparseArray<?> other) {
if (other == null) {
return false;
}
@@ -534,6 +536,9 @@ public class SparseArray<E> implements Cloneable {
}
/**
+ * Returns a hash code value for the contents of this {@link SparseArray}, combining the
+ * {@link Objects#hashCode(Object)} result of all its keys and values.
+ *
* For backwards compatibility, {@link Object#hashCode()} cannot be implemented, so this serves
* as a manually invoked alternative.
*/
diff --git a/core/java/android/util/apk/OWNERS b/core/java/android/util/apk/OWNERS
new file mode 100644
index 000000000000..52c95501e541
--- /dev/null
+++ b/core/java/android/util/apk/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 9473845b15e6..e5a137cd13f3 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -86,6 +86,12 @@ public final class AccessibilityInteractionController {
// accessibility from hanging
private static final long REQUEST_PREPARER_TIMEOUT_MS = 500;
+ // Callbacks should have the same configuration of the flags below to allow satisfying a pending
+ // node request on prefetch
+ private static final int FLAGS_AFFECTING_REPORTED_DATA =
+ AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -114,6 +120,9 @@ public final class AccessibilityInteractionController {
private AddNodeInfosForViewId mAddNodeInfosForViewId;
@GuardedBy("mLock")
+ private ArrayList<Message> mPendingFindNodeByIdMessages;
+
+ @GuardedBy("mLock")
private int mNumActiveRequestPreparers;
@GuardedBy("mLock")
private List<MessageHolder> mMessagesWaitingForRequestPreparer;
@@ -128,6 +137,7 @@ public final class AccessibilityInteractionController {
mViewRootImpl = viewRootImpl;
mPrefetcher = new AccessibilityNodePrefetcher();
mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class);
+ mPendingFindNodeByIdMessages = new ArrayList<>();
}
private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
@@ -177,7 +187,11 @@ public final class AccessibilityInteractionController {
args.arg4 = arguments;
message.obj = args;
- scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
+ synchronized (mLock) {
+ mPendingFindNodeByIdMessages.add(message);
+ scheduleMessage(message, interrogatingPid, interrogatingTid,
+ CONSIDER_REQUEST_PREPARERS);
+ }
}
/**
@@ -315,6 +329,9 @@ public final class AccessibilityInteractionController {
}
private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+ synchronized (mLock) {
+ mPendingFindNodeByIdMessages.remove(message);
+ }
final int flags = message.arg1;
SomeArgs args = (SomeArgs) message.obj;
@@ -329,22 +346,58 @@ public final class AccessibilityInteractionController {
args.recycle();
- List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
- infos.clear();
+ View rootView = null;
+ AccessibilityNodeInfo rootNode = null;
try {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
- final View root = findViewByAccessibilityId(accessibilityViewId);
- if (root != null && isShown(root)) {
- mPrefetcher.prefetchAccessibilityNodeInfos(
- root, virtualDescendantId, flags, infos, arguments);
+ rootView = findViewByAccessibilityId(accessibilityViewId);
+ if (rootView != null && isShown(rootView)) {
+ rootNode = populateAccessibilityNodeInfoForView(
+ rootView, arguments, virtualDescendantId);
}
} finally {
- updateInfosForViewportAndReturnFindNodeResult(
- infos, callback, interactionId, spec, interactiveRegion);
+ updateInfoForViewportAndReturnFindNodeResult(
+ rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode),
+ callback, interactionId, spec, interactiveRegion);
}
+ ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
+ mPrefetcher.prefetchAccessibilityNodeInfos(
+ rootView, rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode),
+ virtualDescendantId, flags, infos);
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ updateInfosForViewPort(infos, spec, interactiveRegion);
+ returnPrefetchResult(interactionId, infos, callback);
+ returnPendingFindAccessibilityNodeInfosInPrefetch(rootNode, infos, flags);
+ }
+
+ private AccessibilityNodeInfo populateAccessibilityNodeInfoForView(
+ View view, Bundle arguments, int virtualViewId) {
+ AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+ // Determine if we'll be populating extra data
+ final String extraDataRequested = (arguments == null) ? null
+ : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
+ AccessibilityNodeInfo root = null;
+ if (provider == null) {
+ root = view.createAccessibilityNodeInfo();
+ if (root != null) {
+ if (extraDataRequested != null) {
+ view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments);
+ }
+ }
+ } else {
+ root = provider.createAccessibilityNodeInfo(virtualViewId);
+ if (root != null) {
+ if (extraDataRequested != null) {
+ provider.addExtraDataToAccessibilityNodeInfo(
+ virtualViewId, root, extraDataRequested, arguments);
+ }
+ }
+ }
+ return root;
}
public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
@@ -383,7 +436,8 @@ public final class AccessibilityInteractionController {
final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
infos.clear();
try {
- if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
+ if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null
+ || viewId == null) {
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
@@ -402,6 +456,7 @@ public final class AccessibilityInteractionController {
mAddNodeInfosForViewId.reset();
}
} finally {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, interactiveRegion);
}
@@ -484,6 +539,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, interactiveRegion);
}
@@ -575,6 +631,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
updateInfoForViewportAndReturnFindNodeResult(
focused, callback, interactionId, spec, interactiveRegion);
}
@@ -629,6 +686,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
updateInfoForViewportAndReturnFindNodeResult(
next, callback, interactionId, spec, interactiveRegion);
}
@@ -785,33 +843,6 @@ public final class AccessibilityInteractionController {
}
}
- private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
- MagnificationSpec spec) {
- if (infos == null) {
- return;
- }
- final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
- if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
- final int infoCount = infos.size();
- for (int i = 0; i < infoCount; i++) {
- AccessibilityNodeInfo info = infos.get(i);
- applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
- }
- }
- }
-
- private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos,
- Region interactiveRegion) {
- if (interactiveRegion == null || infos == null) {
- return;
- }
- final int infoCount = infos.size();
- for (int i = 0; i < infoCount; i++) {
- AccessibilityNodeInfo info = infos.get(i);
- adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
- }
- }
-
private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
Region interactiveRegion) {
if (interactiveRegion == null || info == null) {
@@ -832,17 +863,6 @@ public final class AccessibilityInteractionController {
return false;
}
- private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) {
- if (infos == null || shouldBypassAdjustBoundsInScreen()) {
- return;
- }
- final int infoCount = infos.size();
- for (int i = 0; i < infoCount; i++) {
- final AccessibilityNodeInfo info = infos.get(i);
- adjustBoundsInScreenIfNeeded(info);
- }
- }
-
private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) {
if (info == null || shouldBypassAdjustBoundsInScreen()) {
return;
@@ -890,17 +910,6 @@ public final class AccessibilityInteractionController {
return screenMatrix == null || screenMatrix.isIdentity();
}
- private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
- if (infos == null || shouldBypassAssociateLeashedParent()) {
- return;
- }
- final int infoCount = infos.size();
- for (int i = 0; i < infoCount; i++) {
- final AccessibilityNodeInfo info = infos.get(i);
- associateLeashedParentIfNeeded(info);
- }
- }
-
private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
if (info == null || shouldBypassAssociateLeashedParent()) {
return;
@@ -974,18 +983,46 @@ public final class AccessibilityInteractionController {
return (appScale != 1.0f || (spec != null && !spec.isNop()));
}
+ private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec,
+ Region interactiveRegion) {
+ for (int i = 0; i < infos.size(); i++) {
+ updateInfoForViewPort(infos.get(i), spec, interactiveRegion);
+ }
+ }
+
+ private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec,
+ Region interactiveRegion) {
+ associateLeashedParentIfNeeded(info);
+ applyScreenMatrixIfNeeded(info);
+ adjustBoundsInScreenIfNeeded(info);
+ // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
+ // then impact the visibility result, we need to adjust visibility before apply scale.
+ adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
+ applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
+ }
+
private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
IAccessibilityInteractionConnectionCallback callback, int interactionId,
MagnificationSpec spec, Region interactiveRegion) {
+ if (infos != null) {
+ updateInfosForViewPort(infos, spec, interactiveRegion);
+ }
+ returnFindNodesResult(infos, callback, interactionId);
+ }
+
+ private void returnFindNodeResult(AccessibilityNodeInfo info,
+ IAccessibilityInteractionConnectionCallback callback,
+ int interactionId) {
+ try {
+ callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - the other side will time out */
+ }
+ }
+
+ private void returnFindNodesResult(List<AccessibilityNodeInfo> infos,
+ IAccessibilityInteractionConnectionCallback callback, int interactionId) {
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- associateLeashedParentIfNeeded(infos);
- applyScreenMatrixIfNeeded(infos);
- adjustBoundsInScreenIfNeeded(infos);
- // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
- // then impact the visibility result, we need to adjust visibility before apply scale.
- adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
- applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
if (infos != null) {
infos.clear();
@@ -995,22 +1032,80 @@ public final class AccessibilityInteractionController {
}
}
+ private void returnPendingFindAccessibilityNodeInfosInPrefetch(AccessibilityNodeInfo rootNode,
+ List<AccessibilityNodeInfo> infos, int flags) {
+
+ AccessibilityNodeInfo satisfiedPendingRequestPrefetchedNode = null;
+ IAccessibilityInteractionConnectionCallback satisfiedPendingRequestCallback = null;
+ int satisfiedPendingRequestInteractionId = AccessibilityInteractionClient.NO_ID;
+
+ synchronized (mLock) {
+ for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) {
+ final Message pendingMessage = mPendingFindNodeByIdMessages.get(i);
+ final int pendingFlags = pendingMessage.arg1;
+ if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA)
+ != (flags & FLAGS_AFFECTING_REPORTED_DATA)) {
+ continue;
+ }
+ SomeArgs args = (SomeArgs) pendingMessage.obj;
+ final int accessibilityViewId = args.argi1;
+ final int virtualDescendantId = args.argi2;
+
+ satisfiedPendingRequestPrefetchedNode = nodeWithIdFromList(rootNode,
+ infos, AccessibilityNodeInfo.makeNodeId(
+ accessibilityViewId, virtualDescendantId));
+
+ if (satisfiedPendingRequestPrefetchedNode != null) {
+ satisfiedPendingRequestCallback =
+ (IAccessibilityInteractionConnectionCallback) args.arg1;
+ satisfiedPendingRequestInteractionId = args.argi3;
+ mHandler.removeMessages(
+ PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID,
+ pendingMessage.obj);
+ args.recycle();
+ break;
+ }
+ }
+ mPendingFindNodeByIdMessages.clear();
+ }
+
+ if (satisfiedPendingRequestPrefetchedNode != null) {
+ returnFindNodeResult(
+ AccessibilityNodeInfo.obtain(satisfiedPendingRequestPrefetchedNode),
+ satisfiedPendingRequestCallback, satisfiedPendingRequestInteractionId);
+ }
+ }
+
+ private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo rootNode,
+ List<AccessibilityNodeInfo> infos, long nodeId) {
+ if (rootNode != null && rootNode.getSourceNodeId() == nodeId) {
+ return rootNode;
+ }
+ for (int j = 0; j < infos.size(); j++) {
+ AccessibilityNodeInfo info = infos.get(j);
+ if (info.getSourceNodeId() == nodeId) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos,
+ IAccessibilityInteractionConnectionCallback callback) {
+ if (infos.size() > 0) {
+ try {
+ callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId);
+ } catch (RemoteException re) {
+ /* ignore - other side isn't too bothered if this doesn't arrive */
+ }
+ }
+ }
+
private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
IAccessibilityInteractionConnectionCallback callback, int interactionId,
MagnificationSpec spec, Region interactiveRegion) {
- try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
- associateLeashedParentIfNeeded(info);
- applyScreenMatrixIfNeeded(info);
- adjustBoundsInScreenIfNeeded(info);
- // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
- // then impact the visibility result, we need to adjust visibility before apply scale.
- adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
- applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
- callback.setFindAccessibilityNodeInfoResult(info, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
+ updateInfoForViewPort(info, spec, interactiveRegion);
+ returnFindNodeResult(info, callback, interactionId);
}
private boolean handleClickableSpanActionUiThread(
@@ -1053,56 +1148,45 @@ public final class AccessibilityInteractionController {
private final ArrayList<View> mTempViewList = new ArrayList<View>();
- public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
- List<AccessibilityNodeInfo> outInfos, Bundle arguments) {
+ public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root,
+ int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) {
+ if (root == null) {
+ return;
+ }
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
- // Determine if we'll be populating extra data
- final String extraDataRequested = (arguments == null) ? null
- : arguments.getString(EXTRA_DATA_REQUESTED_KEY);
if (provider == null) {
- AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
- if (root != null) {
- if (extraDataRequested != null) {
- view.addExtraDataToAccessibilityNodeInfo(
- root, extraDataRequested, arguments);
- }
- outInfos.add(root);
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
- prefetchPredecessorsOfRealNode(view, outInfos);
- }
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
- prefetchSiblingsOfRealNode(view, outInfos);
- }
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
- prefetchDescendantsOfRealNode(view, outInfos);
- }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ prefetchPredecessorsOfRealNode(view, outInfos);
+ }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ prefetchSiblingsOfRealNode(view, outInfos);
+ }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ prefetchDescendantsOfRealNode(view, outInfos);
}
} else {
- final AccessibilityNodeInfo root =
- provider.createAccessibilityNodeInfo(virtualViewId);
- if (root != null) {
- if (extraDataRequested != null) {
- provider.addExtraDataToAccessibilityNodeInfo(
- virtualViewId, root, extraDataRequested, arguments);
- }
- outInfos.add(root);
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
- prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
- }
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
- prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
- }
- if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
- prefetchDescendantsOfVirtualNode(root, provider, outInfos);
- }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
+ }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
+ }
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ prefetchDescendantsOfVirtualNode(root, provider, outInfos);
}
}
if (ENFORCE_NODE_TREE_CONSISTENT) {
- enforceNodeTreeConsistent(outInfos);
+ enforceNodeTreeConsistent(root, outInfos);
}
}
- private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
+ private boolean shouldStopPrefetching(List prefetchededInfos) {
+ return mHandler.hasUserInteractiveMessagesWaiting()
+ || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE;
+ }
+
+ private void enforceNodeTreeConsistent(
+ AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) {
LongSparseArray<AccessibilityNodeInfo> nodeMap =
new LongSparseArray<AccessibilityNodeInfo>();
final int nodeCount = nodes.size();
@@ -1113,7 +1197,6 @@ public final class AccessibilityInteractionController {
// If the nodes are a tree it does not matter from
// which node we start to search for the root.
- AccessibilityNodeInfo root = nodeMap.valueAt(0);
AccessibilityNodeInfo parent = root;
while (parent != null) {
root = parent;
@@ -1180,9 +1263,11 @@ public final class AccessibilityInteractionController {
private void prefetchPredecessorsOfRealNode(View view,
List<AccessibilityNodeInfo> outInfos) {
+ if (shouldStopPrefetching(outInfos)) {
+ return;
+ }
ViewParent parent = view.getParentForAccessibility();
- while (parent instanceof View
- && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ while (parent instanceof View && !shouldStopPrefetching(outInfos)) {
View parentView = (View) parent;
AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
if (info != null) {
@@ -1194,6 +1279,9 @@ public final class AccessibilityInteractionController {
private void prefetchSiblingsOfRealNode(View current,
List<AccessibilityNodeInfo> outInfos) {
+ if (shouldStopPrefetching(outInfos)) {
+ return;
+ }
ViewParent parent = current.getParentForAccessibility();
if (parent instanceof ViewGroup) {
ViewGroup parentGroup = (ViewGroup) parent;
@@ -1203,7 +1291,7 @@ public final class AccessibilityInteractionController {
parentGroup.addChildrenForAccessibility(children);
final int childCount = children.size();
for (int i = 0; i < childCount; i++) {
- if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (shouldStopPrefetching(outInfos)) {
return;
}
View child = children.get(i);
@@ -1231,7 +1319,7 @@ public final class AccessibilityInteractionController {
private void prefetchDescendantsOfRealNode(View root,
List<AccessibilityNodeInfo> outInfos) {
- if (!(root instanceof ViewGroup)) {
+ if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) {
return;
}
HashMap<View, AccessibilityNodeInfo> addedChildren =
@@ -1242,7 +1330,7 @@ public final class AccessibilityInteractionController {
root.addChildrenForAccessibility(children);
final int childCount = children.size();
for (int i = 0; i < childCount; i++) {
- if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (shouldStopPrefetching(outInfos)) {
return;
}
View child = children.get(i);
@@ -1267,7 +1355,7 @@ public final class AccessibilityInteractionController {
} finally {
children.clear();
}
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (!shouldStopPrefetching(outInfos)) {
for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {
View addedChild = entry.getKey();
AccessibilityNodeInfo virtualRoot = entry.getValue();
@@ -1289,7 +1377,7 @@ public final class AccessibilityInteractionController {
long parentNodeId = root.getParentNodeId();
int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (shouldStopPrefetching(outInfos)) {
return;
}
final int virtualDescendantId =
@@ -1334,7 +1422,7 @@ public final class AccessibilityInteractionController {
if (parent != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
- if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (shouldStopPrefetching(outInfos)) {
return;
}
final long childNodeId = parent.getChildId(i);
@@ -1359,7 +1447,7 @@ public final class AccessibilityInteractionController {
final int initialOutInfosSize = outInfos.size();
final int childCount = root.getChildCount();
for (int i = 0; i < childCount; i++) {
- if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (shouldStopPrefetching(outInfos)) {
return;
}
final long childNodeId = root.getChildId(i);
@@ -1369,7 +1457,7 @@ public final class AccessibilityInteractionController {
outInfos.add(child);
}
}
- if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
+ if (!shouldStopPrefetching(outInfos)) {
final int addedChildCount = outInfos.size() - initialOutInfosSize;
for (int i = 0; i < addedChildCount; i++) {
AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i);
@@ -1478,6 +1566,10 @@ public final class AccessibilityInteractionController {
boolean hasAccessibilityCallback(Message message) {
return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false;
}
+
+ boolean hasUserInteractiveMessagesWaiting() {
+ return hasMessagesOrCallbacks();
+ }
}
private final class AddNodeInfosForViewId implements Predicate<View> {
diff --git a/core/java/android/view/AppTransitionAnimationSpec.java b/core/java/android/view/AppTransitionAnimationSpec.java
index 877bb5684910..3215f2bc2a50 100644
--- a/core/java/android/view/AppTransitionAnimationSpec.java
+++ b/core/java/android/view/AppTransitionAnimationSpec.java
@@ -28,8 +28,8 @@ public class AppTransitionAnimationSpec implements Parcelable {
public AppTransitionAnimationSpec(Parcel in) {
taskId = in.readInt();
- rect = in.readParcelable(null);
- buffer = in.readParcelable(null);
+ rect = in.readTypedObject(Rect.CREATOR);
+ buffer = in.readTypedObject(HardwareBuffer.CREATOR);
}
@Override
@@ -40,8 +40,8 @@ public class AppTransitionAnimationSpec implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(taskId);
- dest.writeParcelable(rect, 0 /* flags */);
- dest.writeParcelable(buffer, 0);
+ dest.writeTypedObject(rect, 0 /* flags */);
+ dest.writeTypedObject(buffer, 0 /* flags */);
}
public static final @android.annotation.NonNull Parcelable.Creator<AppTransitionAnimationSpec> CREATOR
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 59299f6b15eb..5c65c659e576 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -744,7 +744,10 @@ public final class Choreographer {
}
try {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ "Choreographer#doFrame " + vsyncEventData.id);
+ }
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
new file mode 100644
index 000000000000..5a1b850133cb
--- /dev/null
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -0,0 +1,137 @@
+/**
+ * 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 android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.function.Consumer;
+
+/**
+ * Class that holds all registered {@link CrossWindowBlurEnabledListener}s. It listens
+ * for updates from the WindowManagerService and updates all registered listeners.
+ * @hide
+ */
+public final class CrossWindowBlurListeners {
+ private static final String TAG = "CrossWindowBlurListeners";
+
+ // property for background blur support in surface flinger
+ private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur";
+ public static final boolean CROSS_WINDOW_BLUR_SUPPORTED =
+ SystemProperties.get(BLUR_PROPERTY, "default").equals("1");
+
+ private static volatile CrossWindowBlurListeners sInstance;
+ private static final Object sLock = new Object();
+
+ private final BlurEnabledListenerInternal mListenerInternal = new BlurEnabledListenerInternal();
+ private final ArraySet<Consumer<Boolean>> mListeners = new ArraySet();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private boolean mInternalListenerAttached = false;
+ private boolean mCrossWindowBlurEnabled;
+
+ private CrossWindowBlurListeners() {}
+
+ /**
+ * Returns a CrossWindowBlurListeners instance
+ */
+ public static CrossWindowBlurListeners getInstance() {
+ CrossWindowBlurListeners instance = sInstance;
+ if (instance == null) {
+
+ synchronized (sLock) {
+ instance = sInstance;
+ if (instance == null) {
+ instance = new CrossWindowBlurListeners();
+ sInstance = instance;
+ }
+ }
+ }
+ return instance;
+ }
+
+ boolean isCrossWindowBlurEnabled() {
+ synchronized (sLock) {
+ attachInternalListenerIfNeededLocked();
+ return mCrossWindowBlurEnabled;
+ }
+ }
+
+ void addListener(Consumer<Boolean> listener) {
+ if (listener == null) return;
+
+ synchronized (sLock) {
+ attachInternalListenerIfNeededLocked();
+
+ mListeners.add(listener);
+ notifyListenerOnMain(listener, mCrossWindowBlurEnabled);
+ }
+ }
+
+
+ void removeListener(Consumer<Boolean> listener) {
+ if (listener == null) return;
+
+ synchronized (sLock) {
+ mListeners.remove(listener);
+
+ if (mInternalListenerAttached && mListeners.size() == 0) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .unregisterCrossWindowBlurEnabledListener(mListenerInternal);
+ mInternalListenerAttached = false;
+ } catch (RemoteException e) {
+ Log.d(TAG, "Could not unregister ICrossWindowBlurEnabledListener");
+ }
+ }
+ }
+ }
+
+ private void attachInternalListenerIfNeededLocked() {
+ if (!mInternalListenerAttached) {
+ try {
+ mCrossWindowBlurEnabled = WindowManagerGlobal.getWindowManagerService()
+ .registerCrossWindowBlurEnabledListener(mListenerInternal);
+ mInternalListenerAttached = true;
+ } catch (RemoteException e) {
+ Log.d(TAG, "Could not register ICrossWindowBlurEnabledListener");
+ }
+ }
+ }
+
+ private void notifyListenerOnMain(Consumer<Boolean> listener, boolean enabled) {
+ mMainHandler.post(() -> {
+ listener.accept(enabled);
+ });
+ }
+
+ private final class BlurEnabledListenerInternal extends ICrossWindowBlurEnabledListener.Stub {
+ @Override
+ public void onCrossWindowBlurEnabledChanged(boolean enabled) {
+ synchronized (sLock) {
+ mCrossWindowBlurEnabled = enabled;
+
+ for (int i = 0; i < mListeners.size(); i++) {
+ notifyListenerOnMain(mListeners.valueAt(i), enabled);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0ba1dfee16f3..8117c963b959 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -34,6 +34,7 @@ import android.graphics.ColorSpace;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
@@ -1181,6 +1182,18 @@ public final class Display {
}
/**
+ * Returns the product-specific information about the display or the directly connected
+ * device on the display chain.
+ * For example, if the display is transitively connected, this field may contain product
+ * information about the intermediate device.
+ * Returns {@code null} if product information is not available.
+ */
+ @Nullable
+ public DeviceProductInfo getDeviceProductInfo() {
+ return mDisplayInfo.deviceProductInfo;
+ }
+
+ /**
* Gets display metrics that describe the size and density of this display.
* The size returned by this method does not necessarily represent the
* actual raw size (native resolution) of the display.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 2a00b5a2e513..655f42308a1f 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -810,6 +810,9 @@ public final class DisplayInfo implements Parcelable {
if ((flags & Display.FLAG_TRUSTED) != 0) {
result.append(", FLAG_TRUSTED");
}
+ if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
+ result.append(", FLAG_OWN_DISPLAY_GROUP");
+ }
return result.toString();
}
}
diff --git a/core/java/android/view/ICrossWindowBlurEnabledListener.aidl b/core/java/android/view/ICrossWindowBlurEnabledListener.aidl
new file mode 100644
index 000000000000..69286e20fe17
--- /dev/null
+++ b/core/java/android/view/ICrossWindowBlurEnabledListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Listener to be invoked when cross-window blur is enabled or disabled.
+ * {@hide}
+ */
+oneway interface ICrossWindowBlurEnabledListener {
+ /**
+ * Method that will be invoked when cross-window blur is enabled or disabled.
+ * @param enabled True if cross-window blur is enabled.
+ */
+ void onCrossWindowBlurEnabledChanged(boolean enabled);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index afdf798d03ce..54778007c6ff 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -35,6 +35,7 @@ import android.os.ParcelFileDescriptor;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.ICrossWindowBlurEnabledListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayFoldListener;
@@ -730,7 +731,7 @@ interface IWindowManager
void showGlobalActions();
/**
- * Sets layer tracing flags for SurfaceFlingerTrace.
+ * Sets layer tracing flags for SurfaceFlingerTrace.
*
* @param flags see definition in SurfaceTracing.cpp
*/
@@ -799,4 +800,20 @@ interface IWindowManager
* @param clientToken the window context's token
*/
void unregisterWindowContextListener(IBinder clientToken);
+
+ /**
+ * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
+ *
+ * @param listener the listener to be registered
+ * @return true if cross-window blur is currently enabled; false otherwise
+ */
+ boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
+
+ /**
+ * Unregisters a listener which was registered with
+ * {@link #registerCrossWindowBlurEnabledListener()}.
+ *
+ * @param listener the listener to be unregistered
+ */
+ void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 4a5c95f43d46..d23a1e5992cc 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -116,8 +116,9 @@ public final class ImeFocusController {
if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
return;
}
+ View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
if (DEBUG) {
- Log.v(TAG, "onWindowFocus: " + focusedView
+ Log.v(TAG, "onWindowFocus: " + viewForWindowFocus
+ " softInputMode=" + InputMethodDebug.softInputModeToString(
windowAttribute.softInputMode));
}
@@ -128,8 +129,8 @@ public final class ImeFocusController {
if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true");
forceFocus = true;
}
+
// Update mNextServedView when focusedView changed.
- final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
onViewFocusChanged(viewForWindowFocus, true);
// Starting new input when the next focused view is same as served view but the currently
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/OWNERS b/core/java/android/view/OWNERS
index e66b17aa4426..cbb86de4785f 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -57,6 +57,7 @@ per-file ViewRootImpl.java = file:/graphics/java/android/graphics/OWNERS
per-file ViewRootImpl.java = file:/services/core/java/com/android/server/input/OWNERS
per-file ViewRootImpl.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file ViewRootImpl.java = file:/core/java/android/view/inputmethod/OWNERS
+per-file AccessibilityInteractionController.java = file:/services/accessibility/OWNERS
# WindowManager
per-file DisplayCutout.aidl = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java
index a5ff19ee3312..ea9799584e20 100644
--- a/core/java/android/view/RemoteAnimationDefinition.java
+++ b/core/java/android/view/RemoteAnimationDefinition.java
@@ -184,13 +184,13 @@ public class RemoteAnimationDefinition implements Parcelable {
}
private RemoteAnimationAdapterEntry(Parcel in) {
- adapter = in.readParcelable(RemoteAnimationAdapter.class.getClassLoader());
+ adapter = in.readTypedObject(RemoteAnimationAdapter.CREATOR);
activityTypeFilter = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(adapter, flags);
+ dest.writeTypedObject(adapter, flags);
dest.writeInt(activityTypeFilter);
}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 258a72cbcab4..b1b670f5e0c9 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -222,20 +222,20 @@ public class RemoteAnimationTarget implements Parcelable {
public RemoteAnimationTarget(Parcel in) {
taskId = in.readInt();
mode = in.readInt();
- leash = in.readParcelable(null);
+ leash = in.readTypedObject(SurfaceControl.CREATOR);
isTranslucent = in.readBoolean();
- clipRect = in.readParcelable(null);
- contentInsets = in.readParcelable(null);
+ clipRect = in.readTypedObject(Rect.CREATOR);
+ contentInsets = in.readTypedObject(Rect.CREATOR);
prefixOrderIndex = in.readInt();
- position = in.readParcelable(null);
- localBounds = in.readParcelable(null);
- sourceContainerBounds = in.readParcelable(null);
- screenSpaceBounds = in.readParcelable(null);
- windowConfiguration = in.readParcelable(null);
+ position = in.readTypedObject(Point.CREATOR);
+ localBounds = in.readTypedObject(Rect.CREATOR);
+ sourceContainerBounds = in.readTypedObject(Rect.CREATOR);
+ screenSpaceBounds = in.readTypedObject(Rect.CREATOR);
+ windowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
isNotInRecents = in.readBoolean();
- startLeash = in.readParcelable(null);
- startBounds = in.readParcelable(null);
- pictureInPictureParams = in.readParcelable(null);
+ startLeash = in.readTypedObject(SurfaceControl.CREATOR);
+ startBounds = in.readTypedObject(Rect.CREATOR);
+ pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR);
}
@Override
@@ -247,20 +247,20 @@ public class RemoteAnimationTarget implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(taskId);
dest.writeInt(mode);
- dest.writeParcelable(leash, 0 /* flags */);
+ dest.writeTypedObject(leash, 0 /* flags */);
dest.writeBoolean(isTranslucent);
- dest.writeParcelable(clipRect, 0 /* flags */);
- dest.writeParcelable(contentInsets, 0 /* flags */);
+ dest.writeTypedObject(clipRect, 0 /* flags */);
+ dest.writeTypedObject(contentInsets, 0 /* flags */);
dest.writeInt(prefixOrderIndex);
- dest.writeParcelable(position, 0 /* flags */);
- dest.writeParcelable(localBounds, 0 /* flags */);
- dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
- dest.writeParcelable(screenSpaceBounds, 0 /* flags */);
- dest.writeParcelable(windowConfiguration, 0 /* flags */);
+ dest.writeTypedObject(position, 0 /* flags */);
+ dest.writeTypedObject(localBounds, 0 /* flags */);
+ dest.writeTypedObject(sourceContainerBounds, 0 /* flags */);
+ dest.writeTypedObject(screenSpaceBounds, 0 /* flags */);
+ dest.writeTypedObject(windowConfiguration, 0 /* flags */);
dest.writeBoolean(isNotInRecents);
- dest.writeParcelable(startLeash, 0 /* flags */);
- dest.writeParcelable(startBounds, 0 /* flags */);
- dest.writeParcelable(pictureInPictureParams, 0 /* flags */);
+ dest.writeTypedObject(startLeash, 0 /* flags */);
+ dest.writeTypedObject(startBounds, 0 /* flags */);
+ dest.writeTypedObject(pictureInPictureParams, 0 /* flags */);
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ebef4646b0d9..ab7732b47ca4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22188,9 +22188,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
- // Clear the overscroll effect:
- // TODO: Use internal API instead of overriding the existing RenderEffect
- setRenderEffect(null);
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
/* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
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/Window.java b/core/java/android/view/Window.java
index 221b3346df58..cf5ec8de0362 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1716,13 +1716,22 @@ public abstract class Window {
* For the blur region to be visible, the window has to be translucent. See
* {@link android.R.styleable#Window_windowIsTranslucent}.
*
- * Note the difference with {@link android.view.WindowManager.LayoutParams#blurBehindRadius},
+ * Note the difference with {@link WindowManager.LayoutParams#setBlurBehindRadius},
* which blurs the whole screen behind the window. Background blur blurs the screen behind
* only within the bounds of the window.
*
+ * Some devices might not support cross-window blur due to GPU limitations. It can also be
+ * disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, resulting in a transparent window background. To avoid this, the app might want to
+ * change its theme to one that does not use blurs. To listen for cross-window blur
+ * enabled/disabled events, use {@link WindowManager#addCrossWindowBlurEnabledListener}.
+ *
* @param blurRadius The blur radius to use for window background blur in pixels
*
* @see android.R.styleable#Window_windowBackgroundBlurRadius
+ * @see WindowManager.LayoutParams#setBlurBehindRadius
+ * @see WindowManager#addCrossWindowBlurEnabledListener
*/
public void setBackgroundBlurRadius(int blurRadius) {}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 9e87c95a8a75..7e9a850178fc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -82,6 +82,7 @@ import static android.view.WindowLayoutParamsProto.Y;
import android.Manifest.permission;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -120,6 +121,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* The interface that apps use to talk to the window manager.
@@ -611,6 +613,10 @@ public interface WindowManager extends ViewManager {
* For example, for activities in multi-window mode, the metrics returned are based on the
* current bounds that the user has selected for the {@link android.app.Activity Activity}'s
* task.
+ * <p>
+ * In most scenarios, {@link #getCurrentWindowMetrics()} rather than
+ * {@link #getMaximumWindowMetrics()} is the correct API to use, since it ensures values reflect
+ * window size when the app is not fullscreen.
*
* @see #getMaximumWindowMetrics()
* @see WindowMetrics
@@ -622,26 +628,27 @@ public interface WindowManager extends ViewManager {
/**
* Returns the largest {@link WindowMetrics} an app may expect in the current system state.
* <p>
- * The metrics describe the size of the largest potential area the window might occupy with
- * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
- * such a window would have.
- * <p>
* The value of this is based on the largest <b>potential</b> windowing state of the system.
*
* For example, for activities in multi-window mode, the metrics returned are based on the
* what the bounds would be if the user expanded the {@link android.app.Activity Activity}'s
* task to cover the entire screen.
- *
+ * <p>
+ * The metrics describe the size of the largest potential area the window might occupy with
+ * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
+ * such a window would have.
+ * <p>
* Note that this might still be smaller than the size of the physical display if certain areas
* of the display are not available to windows created in this {@link Context}.
- * <p>
+ *
* For example, given that there's a device which have a multi-task mode to limit activities
* to a half screen. In this case, {@link #getMaximumWindowMetrics()} reports the bounds of
- * the half screen which the activity is located, while {@link Display#getRealSize(Point)} still
- * reports the bounds of the whole physical display.
- *
- * Despite this, {@link #getMaximumWindowMetrics()} and {@link Display#getRealSize(Point)}
- * reports the same bounds in general.
+ * the half screen which the activity is located.
+ * <p>
+ * <b>Generally {@link #getCurrentWindowMetrics()} is the correct API to use</b> for choosing
+ * UI layouts. {@link #getMaximumWindowMetrics()} are only appropriate when the application
+ * needs to know the largest possible size it can occupy if the user expands/maximizes it on the
+ * screen.
*
* @see #getCurrentWindowMetrics()
* @see WindowMetrics
@@ -808,6 +815,64 @@ public interface WindowManager extends ViewManager {
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
+ /**
+ * Returns whether cross-window blur is currently enabled. This affects both window blur behind
+ * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur (see
+ * {@link Window#setBackgroundBlurRadius}).
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+ * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+ * change its theme to one that does not use blurs. To listen for cross-window blur
+ * enabled/disabled events, use {@link #addCrossWindowBlurEnabledListener}.
+ *
+ * @see #addCrossWindowBlurEnabledListener
+ * @see LayoutParams#setBlurBehindRadius
+ * @see Window#setBackgroundBlurRadius
+ */
+ default boolean isCrossWindowBlurEnabled() {
+ return false;
+ }
+
+ /**
+ * Adds a listener, which will be called when cross-window blurs are enabled/disabled at
+ * runtime. This affects both window blur behind (see {@link LayoutParams#setBlurBehindRadius})
+ * and window background blur (see {@link Window#setBackgroundBlurRadius}).
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can also
+ * be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling is used or
+ * when minimal post processing is requested. In such situations, no blur will be computed or
+ * drawn, so the blur target area will not be blurred. To handle this, the app might want to
+ * change its theme to one that does not use blurs.
+ *
+ * The listener will be called on the main thread.
+ *
+ * If the listener is added successfully, it will be called immediately with the current
+ * cross-window blur enabled state.
+ *
+ *
+ * @param listener the listener to be added. It will be called back with a boolean parameter,
+ * which is true if cross-window blur is enabled and false if it is disabled
+ *
+ * @see #removeCrossWindowBlurEnabledListener
+ * @see #isCrossWindowBlurEnabled
+ * @see LayoutParams#setBlurBehindRadius
+ * @see Window#setBackgroundBlurRadius
+ */
+ default void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ }
+
+ /**
+ * Removes a listener, previously added with {@link #addCrossWindowBlurEnabledListener}
+ *
+ * @param listener the listener to be removed
+ *
+ * @see #addCrossWindowBlurEnabledListener
+ */
+ default void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ }
+
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
@@ -2722,6 +2787,15 @@ public interface WindowManager extends ViewManager {
public boolean hasManualSurfaceInsets;
/**
+ * Whether we should use global insets state when report insets to the window. When set to
+ * {@code true}, all the insets will be reported to the window regardless of the z-order.
+ * Otherwise, only the insets above the given window will be reported.
+ *
+ * @hide
+ */
+ public boolean receiveInsetsIgnoringZOrder;
+
+ /**
* Whether the previous surface insets should be used vs. what is currently set. When set
* to {@code true}, the view root will ignore surfaces insets in this object and use what
* it currently has.
@@ -3238,9 +3312,9 @@ public interface WindowManager extends ViewManager {
* The blur behind radius range starts at 0, which means no blur, and increases until 150
* for the densest blur.
*
- * @see #FLAG_BLUR_BEHIND
+ * @see #setBlurBehindRadius
*/
- public int blurBehindRadius = 0;
+ private int mBlurBehindRadius = 0;
/**
* The color mode requested by this window. The target display may
@@ -3534,6 +3608,50 @@ public interface WindowManager extends ViewManager {
return mColorMode;
}
+ /**
+ * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount},
+ * but instead of dimmed, the content behind the window will be blurred (or combined with
+ * the dim amount, if such is specified).
+ *
+ * The density of the blur is set by the blur radius. The radius defines the size
+ * of the neighbouring area, from which pixels will be averaged to form the final
+ * color for each pixel. The operation approximates a Gaussian blur.
+ * A radius of 0 means no blur. The higher the radius, the denser the blur.
+ *
+ * Note the difference with {@link android.view.Window#setBackgroundBlurRadius},
+ * which blurs only within the bounds of the window. Blur behind blurs the whole screen
+ * behind the window.
+ *
+ * Requires {@link #FLAG_BLUR_BEHIND} to be set.
+ *
+ * Cross-window blur might not be supported by some devices due to GPU limitations. It can
+ * also be disabled at runtime, e.g. during battery saving mode, when multimedia tunneling
+ * is used or when minimal post processing is requested. In such situations, no blur will
+ * be computed or drawn, resulting in there being no depth separation between the window
+ * and the content behind it. To avoid this, the app might want to use more
+ * {@link #dimAmount} on its window. To listen for cross-window blur enabled/disabled
+ * events, use {@link #addCrossWindowBlurEnabledListener}.
+ *
+ * @param blurBehindRadius The blur radius to use for blur behind in pixels
+ *
+ * @see #FLAG_BLUR_BEHIND
+ * @see #getBlurBehindRadius
+ * @see WindowManager#addCrossWindowBlurEnabledListener
+ * @see Window#setBackgroundBlurRadius
+ */
+ public void setBlurBehindRadius(@IntRange(from = 0) int blurBehindRadius) {
+ mBlurBehindRadius = blurBehindRadius;
+ }
+
+ /**
+ * Returns the blur behind radius of the window.
+ *
+ * @see #setBlurBehindRadius
+ */
+ public int getBlurBehindRadius() {
+ return mBlurBehindRadius;
+ }
+
/** @hide */
@SystemApi
public final void setUserActivityTimeout(long timeout) {
@@ -3610,15 +3728,16 @@ public interface WindowManager extends ViewManager {
out.writeInt(preferredDisplayModeId);
out.writeInt(systemUiVisibility);
out.writeInt(subtreeSystemUiVisibility);
- out.writeInt(hasSystemUiListeners ? 1 : 0);
+ out.writeBoolean(hasSystemUiListeners);
out.writeInt(inputFeatures);
out.writeLong(userActivityTimeout);
out.writeInt(surfaceInsets.left);
out.writeInt(surfaceInsets.top);
out.writeInt(surfaceInsets.right);
out.writeInt(surfaceInsets.bottom);
- out.writeInt(hasManualSurfaceInsets ? 1 : 0);
- out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
+ out.writeBoolean(hasManualSurfaceInsets);
+ out.writeBoolean(receiveInsetsIgnoringZOrder);
+ out.writeBoolean(preservePreviousSurfaceInsets);
out.writeLong(accessibilityIdOfAnchor);
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
@@ -3629,7 +3748,7 @@ public interface WindowManager extends ViewManager {
out.writeInt(mFitInsetsSides);
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
- out.writeInt(blurBehindRadius);
+ out.writeInt(mBlurBehindRadius);
if (providesInsetsTypes != null) {
out.writeInt(providesInsetsTypes.length);
out.writeIntArray(providesInsetsTypes);
@@ -3679,15 +3798,16 @@ public interface WindowManager extends ViewManager {
preferredDisplayModeId = in.readInt();
systemUiVisibility = in.readInt();
subtreeSystemUiVisibility = in.readInt();
- hasSystemUiListeners = in.readInt() != 0;
+ hasSystemUiListeners = in.readBoolean();
inputFeatures = in.readInt();
userActivityTimeout = in.readLong();
surfaceInsets.left = in.readInt();
surfaceInsets.top = in.readInt();
surfaceInsets.right = in.readInt();
surfaceInsets.bottom = in.readInt();
- hasManualSurfaceInsets = in.readInt() != 0;
- preservePreviousSurfaceInsets = in.readInt() != 0;
+ hasManualSurfaceInsets = in.readBoolean();
+ receiveInsetsIgnoringZOrder = in.readBoolean();
+ preservePreviousSurfaceInsets = in.readBoolean();
accessibilityIdOfAnchor = in.readLong();
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
@@ -3698,7 +3818,7 @@ public interface WindowManager extends ViewManager {
mFitInsetsSides = in.readInt();
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
- blurBehindRadius = in.readInt();
+ mBlurBehindRadius = in.readInt();
int insetsTypesLength = in.readInt();
if (insetsTypesLength > 0) {
providesInsetsTypes = new int[insetsTypesLength];
@@ -3752,7 +3872,7 @@ public interface WindowManager extends ViewManager {
/** {@hide} */
public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28;
/** {@hide} */
- public static final int BACKGROUND_BLUR_RADIUS_CHANGED = 1 << 29;
+ public static final int BLUR_BEHIND_RADIUS_CHANGED = 1 << 29;
// internal buffer to backup/restore parameters under compatibility mode.
private int[] mCompatibilityParamsBackup = null;
@@ -3916,6 +4036,11 @@ public interface WindowManager extends ViewManager {
changes |= SURFACE_INSETS_CHANGED;
}
+ if (receiveInsetsIgnoringZOrder != o.receiveInsetsIgnoringZOrder) {
+ receiveInsetsIgnoringZOrder = o.receiveInsetsIgnoringZOrder;
+ changes |= SURFACE_INSETS_CHANGED;
+ }
+
if (preservePreviousSurfaceInsets != o.preservePreviousSurfaceInsets) {
preservePreviousSurfaceInsets = o.preservePreviousSurfaceInsets;
changes |= SURFACE_INSETS_CHANGED;
@@ -3943,9 +4068,9 @@ public interface WindowManager extends ViewManager {
changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
}
- if (blurBehindRadius != o.blurBehindRadius) {
- blurBehindRadius = o.blurBehindRadius;
- changes |= BACKGROUND_BLUR_RADIUS_CHANGED;
+ if (mBlurBehindRadius != o.mBlurBehindRadius) {
+ mBlurBehindRadius = o.mBlurBehindRadius;
+ changes |= BLUR_BEHIND_RADIUS_CHANGED;
}
// This can't change, it's only set at window creation time.
@@ -4104,6 +4229,9 @@ public interface WindowManager extends ViewManager {
sb.append(" (!preservePreviousSurfaceInsets)");
}
}
+ if (receiveInsetsIgnoringZOrder) {
+ sb.append(" receive insets ignoring z-order");
+ }
if (mColorMode != COLOR_MODE_DEFAULT) {
sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
}
@@ -4111,9 +4239,9 @@ public interface WindowManager extends ViewManager {
sb.append(" preferMinimalPostProcessing=");
sb.append(preferMinimalPostProcessing);
}
- if (blurBehindRadius != 0) {
+ if (mBlurBehindRadius != 0) {
sb.append(" blurBehindRadius=");
- sb.append(blurBehindRadius);
+ sb.append(mBlurBehindRadius);
}
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 23842b3a41ac..b39870738d68 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import java.util.List;
+import java.util.function.Consumer;
/**
* Provides low-level communication with the system window manager for
@@ -301,4 +302,19 @@ public final class WindowManagerImpl implements WindowManager {
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public boolean isCrossWindowBlurEnabled() {
+ return CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled();
+ }
+
+ @Override
+ public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ CrossWindowBlurListeners.getInstance().addListener(listener);
+ }
+
+ @Override
+ public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+ CrossWindowBlurListeners.getInstance().removeListener(listener);
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7cc2db1c0dab..72d403e1f867 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -149,8 +149,15 @@ public class WindowlessWindowManager implements IWindowSession {
if (((attrs.inputFeatures &
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
try {
- mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
+ if(mRealWm instanceof IWindowSession.Stub) {
+ mRealWm.grantInputChannel(displayId,
+ new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
+ window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type,
+ outInputChannel);
+ } else {
+ mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
attrs.privateFlags, attrs.type, outInputChannel);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to grant input to surface: ", e);
}
@@ -293,8 +300,14 @@ public class WindowlessWindowManager implements IWindowSession {
if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
&& state.mInputChannelToken != null) {
try {
- mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
- attrs.flags, attrs.privateFlags, state.mInputRegion);
+ if(mRealWm instanceof IWindowSession.Stub) {
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
+ new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
+ attrs.flags, attrs.privateFlags, state.mInputRegion);
+ } else {
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
+ attrs.flags, attrs.privateFlags, state.mInputRegion);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to update surface input channel: ", e);
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index f63749be6df2..8d1271d7311c 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -23,7 +23,9 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -113,6 +115,8 @@ public final class AccessibilityInteractionClient
private final Object mInstanceLock = new Object();
+ private Handler mMainHandler;
+
private volatile int mInteractionId = -1;
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
@@ -123,6 +127,11 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
+ private int mInteractionIdWaitingForPrefetchResult;
+ private int mConnectionIdWaitingForPrefetchResult;
+ private String[] mPackageNamesForNextPrefetchResult;
+ private Runnable mPrefetchResultRunnable;
+
/**
* @return The client for the current thread.
*/
@@ -197,6 +206,10 @@ public final class AccessibilityInteractionClient
private AccessibilityInteractionClient() {
/* reducing constructor visibility */
+ Looper mainLooper = Looper.getMainLooper();
+ if (mainLooper != null) {
+ mMainHandler = new Handler(mainLooper);
+ }
}
/**
@@ -451,16 +464,16 @@ public final class AccessibilityInteractionClient
Binder.restoreCallingIdentity(identityToken);
}
if (packageNames != null) {
- List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
- interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
- bypassCache, packageNames);
- if (infos != null && !infos.isEmpty()) {
- for (int i = 1; i < infos.size(); i++) {
- infos.get(i).recycle();
- }
- return infos.get(0);
+ AccessibilityNodeInfo info =
+ getFindAccessibilityNodeInfoResultAndClear(interactionId);
+ if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
+ && info != null) {
+ setInteractionWaitingForPrefetchResult(interactionId, connectionId,
+ packageNames);
}
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId,
+ bypassCache, packageNames);
+ return info;
}
} else {
if (DEBUG) {
@@ -474,6 +487,15 @@ public final class AccessibilityInteractionClient
return null;
}
+ private void setInteractionWaitingForPrefetchResult(int interactionId, int connectionId,
+ String[] packageNames) {
+ synchronized (mInstanceLock) {
+ mInteractionIdWaitingForPrefetchResult = interactionId;
+ mConnectionIdWaitingForPrefetchResult = connectionId;
+ mPackageNamesForNextPrefetchResult = packageNames;
+ }
+ }
+
private static String idToString(int accessibilityWindowId, long accessibilityNodeId) {
return accessibilityWindowId + "/"
+ AccessibilityNodeInfo.idToString(accessibilityNodeId);
@@ -829,6 +851,59 @@ public final class AccessibilityInteractionClient
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setPrefetchAccessibilityNodeInfoResult(@NonNull List<AccessibilityNodeInfo> infos,
+ int interactionId) {
+ List<AccessibilityNodeInfo> infosCopy = null;
+ int mConnectionIdWaitingForPrefetchResultCopy = -1;
+ String[] mPackageNamesForNextPrefetchResultCopy = null;
+
+ synchronized (mInstanceLock) {
+ if (!infos.isEmpty() && mInteractionIdWaitingForPrefetchResult == interactionId) {
+ if (mMainHandler != null) {
+ if (mPrefetchResultRunnable != null) {
+ mMainHandler.removeCallbacks(mPrefetchResultRunnable);
+ mPrefetchResultRunnable = null;
+ }
+ /**
+ * TODO(b/180957109): AccessibilityCache is prone to deadlocks
+ * We post caching the prefetched nodes in the main thread. Using the binder
+ * thread results in "Long monitor contention with owner main" logs where
+ * service response times may exceed 5 seconds. This is due to the cache calling
+ * out to the system when refreshing nodes with the lock held.
+ */
+ mPrefetchResultRunnable = () -> finalizeAndCacheAccessibilityNodeInfos(
+ infos, mConnectionIdWaitingForPrefetchResult, false,
+ mPackageNamesForNextPrefetchResult);
+ mMainHandler.post(mPrefetchResultRunnable);
+
+ } else {
+ for (AccessibilityNodeInfo info : infos) {
+ infosCopy.add(new AccessibilityNodeInfo(info));
+ }
+ mConnectionIdWaitingForPrefetchResultCopy =
+ mConnectionIdWaitingForPrefetchResult;
+ mPackageNamesForNextPrefetchResultCopy =
+ new String[mPackageNamesForNextPrefetchResult.length];
+ for (int i = 0; i < mPackageNamesForNextPrefetchResult.length; i++) {
+ mPackageNamesForNextPrefetchResultCopy[i] =
+ mPackageNamesForNextPrefetchResult[i];
+ }
+ }
+ }
+
+ }
+
+ if (infosCopy != null) {
+ finalizeAndCacheAccessibilityNodeInfos(
+ infosCopy, mConnectionIdWaitingForPrefetchResultCopy, false,
+ mPackageNamesForNextPrefetchResultCopy);
+ }
+ }
+
+ /**
* Gets the result of a request to perform an accessibility action.
*
* @param interactionId The interaction id to match the result with the request.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 97ce92cd90aa..ab46170792a5 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1781,8 +1781,12 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param viewId The fully qualified resource name of the view id to find.
* @return A list of node info.
*/
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(@NonNull String viewId) {
enforceSealed();
+ if (viewId == null) {
+ Log.e(TAG, "returns empty list due to null viewId.");
+ return Collections.emptyList();
+ }
if (!canPerformRequestOverConnection(mConnectionId, mWindowId, mSourceNodeId)) {
return Collections.emptyList();
}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index 049bb31adbb1..231e75a19a06 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -47,6 +47,15 @@ oneway interface IAccessibilityInteractionConnectionCallback {
int interactionId);
/**
+ * Sets the result of a prefetch request that returns {@link AccessibilityNodeInfo}s.
+ *
+ * @param root The {@link AccessibilityNodeInfo} for which the prefetching is based off of.
+ * @param infos The result {@link AccessibilityNodeInfo}s.
+ */
+ void setPrefetchAccessibilityNodeInfoResult(
+ in List<AccessibilityNodeInfo> infos, int interactionId);
+
+ /**
* Sets the result of a request to perform an accessibility action.
*
* @param Whether the action was performed.
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9bf3626fe868..9998fbc02d12 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.TaskInfo;
+import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.Context;
import android.content.LocusId;
@@ -36,6 +37,8 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
/**
* Context associated with a {@link ContentCaptureSession} - see {@link ContentCaptureManager} for
* more info.
@@ -99,16 +102,17 @@ public final class ContentCaptureContext implements Parcelable {
// Fields below are set by server when the session starts
private final @Nullable ComponentName mComponentName;
- private final int mTaskId;
private final int mFlags;
private final int mDisplayId;
+ private final ActivityId mActivityId;
// Fields below are set by the service upon "delivery" and are not marshalled in the parcel
private int mParentSessionId = NO_SESSION_ID;
/** @hide */
public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
- @NonNull ComponentName componentName, int taskId, int displayId, int flags) {
+ @NonNull ActivityId activityId, @NonNull ComponentName componentName, int displayId,
+ int flags) {
if (clientContext != null) {
mHasClientContext = true;
mExtras = clientContext.mExtras;
@@ -118,10 +122,10 @@ public final class ContentCaptureContext implements Parcelable {
mExtras = null;
mId = null;
}
- mComponentName = Preconditions.checkNotNull(componentName);
- mTaskId = taskId;
- mDisplayId = displayId;
+ mComponentName = Objects.requireNonNull(componentName);
mFlags = flags;
+ mDisplayId = displayId;
+ mActivityId = activityId;
}
private ContentCaptureContext(@NonNull Builder builder) {
@@ -130,8 +134,9 @@ public final class ContentCaptureContext implements Parcelable {
mId = builder.mId;
mComponentName = null;
- mTaskId = mFlags = 0;
+ mFlags = 0;
mDisplayId = Display.INVALID_DISPLAY;
+ mActivityId = null;
}
/** @hide */
@@ -140,9 +145,9 @@ public final class ContentCaptureContext implements Parcelable {
mExtras = original.mExtras;
mId = original.mId;
mComponentName = original.mComponentName;
- mTaskId = original.mTaskId;
mFlags = original.mFlags | extraFlags;
mDisplayId = original.mDisplayId;
+ mActivityId = original.mActivityId;
}
/**
@@ -170,7 +175,7 @@ public final class ContentCaptureContext implements Parcelable {
*/
@SystemApi
public int getTaskId() {
- return mTaskId;
+ return mHasClientContext ? 0 : mActivityId.getTaskId();
}
/**
@@ -184,6 +189,18 @@ public final class ContentCaptureContext implements Parcelable {
}
/**
+ * Gets the Activity id information associated with this context, or {@code null} when it is a
+ * child session.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public ActivityId getActivityId() {
+ return mHasClientContext ? null : mActivityId;
+ }
+
+ /**
* Gets the id of the session that originated this session (through
* {@link ContentCaptureSession#createContentCaptureSession(ContentCaptureContext)}),
* or {@code null} if this is the main session associated with the Activity's {@link Context}.
@@ -309,7 +326,7 @@ public final class ContentCaptureContext implements Parcelable {
if (mId != null) {
pw.print(", id="); mId.dump(pw);
}
- pw.print(", taskId="); pw.print(mTaskId);
+ pw.print(", activityId="); pw.print(mActivityId);
pw.print(", displayId="); pw.print(mDisplayId);
if (mParentSessionId != NO_SESSION_ID) {
pw.print(", parentId="); pw.print(mParentSessionId);
@@ -333,7 +350,7 @@ public final class ContentCaptureContext implements Parcelable {
if (fromServer()) {
builder.append("act=").append(ComponentName.flattenToShortString(mComponentName))
- .append(", taskId=").append(mTaskId)
+ .append(", activityId=").append(mActivityId)
.append(", displayId=").append(mDisplayId)
.append(", flags=").append(mFlags);
} else {
@@ -363,9 +380,9 @@ public final class ContentCaptureContext implements Parcelable {
}
parcel.writeParcelable(mComponentName, flags);
if (fromServer()) {
- parcel.writeInt(mTaskId);
parcel.writeInt(mDisplayId);
parcel.writeInt(mFlags);
+ mActivityId.writeToParcel(parcel, flags);
}
}
@@ -393,11 +410,12 @@ public final class ContentCaptureContext implements Parcelable {
// Client-state only
return clientContext;
} else {
- final int taskId = parcel.readInt();
final int displayId = parcel.readInt();
final int flags = parcel.readInt();
- return new ContentCaptureContext(clientContext, componentName, taskId, displayId,
- flags);
+ final ActivityId activityId = new ActivityId(parcel);
+
+ return new ContentCaptureContext(clientContext, activityId, componentName,
+ displayId, flags);
}
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 46e0306cefc8..9523bcdb8e39 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -435,10 +435,11 @@ public final class ContentCaptureManager {
/** @hide */
@UiThread
public void onActivityCreated(@NonNull IBinder applicationToken,
- @NonNull ComponentName activityComponent) {
+ @NonNull IBinder shareableActivityToken, @NonNull ComponentName activityComponent) {
if (mOptions.lite) return;
synchronized (mLock) {
- getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
+ getMainContentCaptureSession().start(applicationToken, shareableActivityToken,
+ activityComponent, mFlags);
}
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index ef8295c30fdf..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;
@@ -44,8 +45,9 @@ oneway interface IContentCaptureManager {
* @param flags Meta flags that enable or disable content capture (see
* {@link IContentCaptureContext#flags}).
*/
- void startSession(IBinder activityToken, in ComponentName componentName,
- int sessionId, int flags, in IResultReceiver result);
+ void startSession(IBinder activityToken, IBinder shareableActivityToken,
+ in ComponentName componentName, int sessionId, int flags,
+ in IResultReceiver result);
/**
* Marks the end of a session for the calling user identified by
@@ -100,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/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 3c18b6b89af8..5ca793e3c394 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -123,6 +123,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
@Nullable
private IBinder mApplicationToken;
+ @Nullable
+ private IBinder mShareableActivityToken;
@Nullable
private ComponentName mComponentName;
@@ -217,8 +219,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
* Starts this session.
*/
@UiThread
- void start(@NonNull IBinder token, @NonNull ComponentName component,
- int flags) {
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
if (!isContentCaptureEnabled()) return;
if (sVerbose) {
@@ -237,6 +239,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
mState = STATE_WAITING_FOR_SERVER;
mApplicationToken = token;
+ mShareableActivityToken = shareableActivityToken;
mComponentName = component;
if (sVerbose) {
@@ -245,8 +248,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
try {
- mSystemServerInterface.startSession(mApplicationToken, component, mId, flags,
- mSessionStateReceiver);
+ mSystemServerInterface.startSession(mApplicationToken, mShareableActivityToken,
+ component, mId, flags, mSessionStateReceiver);
} catch (RemoteException e) {
Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
}
@@ -583,6 +586,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
mDisabled.set((newState & STATE_DISABLED) != 0);
// TODO(b/122454205): must reset children (which currently is owned by superclass)
mApplicationToken = null;
+ mShareableActivityToken = null;
mComponentName = null;
mEvents = null;
if (mDirectServiceInterface != null) {
@@ -721,6 +725,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
if (mApplicationToken != null) {
pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
}
+ if (mShareableActivityToken != null) {
+ pw.print(prefix); pw.print("sharable activity token: ");
+ pw.println(mShareableActivityToken);
+ }
if (mComponentName != null) {
pw.print(prefix); pw.print("component name: ");
pw.println(mComponentName.flattenToShortString());
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/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index e1754531d761..872e15e3d43e 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -36,6 +36,10 @@ oneway interface ITranslationManager {
int sessionId, in IResultReceiver receiver, int userId);
void updateUiTranslationState(int state, in TranslationSpec sourceSpec,
- in TranslationSpec destSpec, in List<AutofillId> viewIds, in int taskId,
+ in TranslationSpec destSpec, in List<AutofillId> viewIds, IBinder token, int taskId,
+ int userId);
+ // deprecated
+ void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
+ in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
int userId);
}
diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java
index ab1bc477e0fd..16418a78f2c1 100644
--- a/core/java/android/view/translation/TranslationSpec.java
+++ b/core/java/android/view/translation/TranslationSpec.java
@@ -28,7 +28,7 @@ import com.android.internal.util.DataClass;
* <p>This spec help specify information such as the language/locale for the translation, as well
* as the data format for the translation (text, audio, etc.)</p>
*/
-@DataClass(genEqualsHashCode = true, genHiddenConstDefs = true)
+@DataClass(genEqualsHashCode = true, genHiddenConstDefs = true, genToString = true)
public final class TranslationSpec implements Parcelable {
/** Data format for translation is text. */
@@ -99,6 +99,18 @@ public final class TranslationSpec implements Parcelable {
@Override
@DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "TranslationSpec { " +
+ "language = " + mLanguage + ", " +
+ "dataFormat = " + mDataFormat +
+ " }";
+ }
+
+ @Override
+ @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(TranslationSpec other) { ... }
@@ -175,10 +187,10 @@ public final class TranslationSpec implements Parcelable {
};
@DataClass.Generated(
- time = 1609964630624L,
+ time = 1614326090637L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationSpec.java",
- inputSignatures = "public static final int DATA_FORMAT_TEXT\nprivate final @android.annotation.NonNull java.lang.String mLanguage\nprivate final @android.view.translation.TranslationSpec.DataFormat int mDataFormat\nclass TranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final int DATA_FORMAT_TEXT\nprivate final @android.annotation.NonNull java.lang.String mLanguage\nprivate final @android.view.translation.TranslationSpec.DataFormat int mDataFormat\nclass TranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 22c3e57ecc95..163f832882c5 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -35,6 +35,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.SyncResultReceiver;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -221,6 +222,12 @@ public class Translator {
return mId;
}
+ /** @hide */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("sourceSpec: "); pw.println(mSourceSpec);
+ pw.print(prefix); pw.print("destSpec: "); pw.println(mDestSpec);
+ }
+
/**
* Requests a translation for the provided {@link TranslationRequest} using the Translator's
* source spec and destination spec.
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index b49d3c004f44..81006121bdef 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -32,11 +32,15 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.translation.UiTranslationManager.UiTranslationState;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -130,6 +134,23 @@ public class UiTranslationController {
}
/**
+ * Called to dump the translation information for Activity.
+ */
+ public void dump(String outerPrefix, PrintWriter pw) {
+ pw.print(outerPrefix); pw.println("UiTranslationController:");
+ final String pfx = outerPrefix + " ";
+ pw.print(pfx); pw.print("activity: "); pw.println(mActivity);
+ final int translatorSize = mTranslators.size();
+ pw.print(outerPrefix); pw.print("number translator: "); pw.println(translatorSize);
+ for (int i = 0; i < translatorSize; i++) {
+ pw.print(outerPrefix); pw.print("#"); pw.println(i);
+ final Translator translator = mTranslators.valueAt(i);
+ translator.dump(outerPrefix, pw);
+ pw.println();
+ }
+ }
+
+ /**
* The method is used by {@link Translator}, it will be called when the translation is done. The
* translation result can be get from here.
*/
@@ -194,24 +215,19 @@ public class UiTranslationController {
private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
synchronized (mLock) {
// Find Views collect the translation data
- // TODO(b/178084101): try to optimize, e.g. to this in a single traversal
- final int viewCounts = views.size();
final ArrayList<TranslationRequest> requests = new ArrayList<>();
- for (int i = 0; i < viewCounts; i++) {
- final AutofillId viewAutofillId = views.get(i);
- final View view = mActivity.findViewByAutofillIdTraversal(viewAutofillId);
- if (view == null) {
- Log.w(TAG, "Can not find the View for autofill id= " + viewAutofillId);
- continue;
- }
- mViews.put(viewAutofillId, new WeakReference<>(view));
+ final ArrayList<View> foundViews = new ArrayList<>();
+ findViewsTraversalByAutofillIds(views, foundViews);
+ for (int i = 0; i < foundViews.size(); i++) {
+ final View view = foundViews.get(i);
+ final int currentCount = i;
mActivity.runOnUiThread(() -> {
final TranslationRequest translationRequest = view.onCreateTranslationRequest();
if (translationRequest != null
&& translationRequest.getTranslationText().length() > 0) {
requests.add(translationRequest);
}
- if (requests.size() == viewCounts) {
+ if (currentCount == (foundViews.size() - 1)) {
Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request.");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
@@ -222,6 +238,42 @@ public class UiTranslationController {
}
}
+ private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds,
+ ArrayList<View> foundViews) {
+ final ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+ if (rootView instanceof ViewGroup) {
+ findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds, foundViews);
+ } else {
+ addViewIfNeeded(sourceViewIds, rootView, foundViews);
+ }
+ }
+ }
+
+ private void findViewsTraversalByAutofillIds(ViewGroup viewGroup,
+ List<AutofillId> sourceViewIds, ArrayList<View> foundViews) {
+ final int childCount = viewGroup.getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ final View child = viewGroup.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds, foundViews);
+ } else {
+ addViewIfNeeded(sourceViewIds, child, foundViews);
+ }
+ }
+ }
+
+ private void addViewIfNeeded(List<AutofillId> sourceViewIds, View view,
+ ArrayList<View> foundViews) {
+ final AutofillId autofillId = view.getAutofillId();
+ if (sourceViewIds.contains(autofillId)) {
+ mViews.put(autofillId, new WeakReference<>(view));
+ foundViews.add(view);
+ }
+ }
+
private void runForEachView(Consumer<View> action) {
synchronized (mLock) {
final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index eeb463ae0ed3..a3a6a2e52138 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.app.assist.ActivityId;
import android.content.Context;
import android.os.RemoteException;
import android.view.View;
@@ -95,26 +96,61 @@ public final class UiTranslationManager {
/**
* Request ui translation for a given Views.
*
+ * NOTE: Please use {@code startTranslation(TranslationSpec, TranslationSpec, List<AutofillId>,
+ * ActivityId)} instead.
+ *
* @param sourceSpec {@link TranslationSpec} for the data to be translated.
* @param destSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
*/
+ // TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
- // TODO(b/177789967): Return result code or find a way to notify the status.
- // TODO(b/177394471): The is a temparary API, the expected is requestUiTranslation(
- // TranslationSpec, TranslationSpec,List<AutofillId>, Binder). We may need more time to
- // implement it, use task id as initial version for demo.
Objects.requireNonNull(sourceSpec);
Objects.requireNonNull(destSpec);
Objects.requireNonNull(viewIds);
+ if (viewIds.size() == 0) {
+ throw new IllegalArgumentException("Invalid empty views: " + viewIds);
+ }
+ try {
+ mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_STARTED, sourceSpec,
+ destSpec, viewIds, taskId, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ /**
+ * Request ui translation for a given Views.
+ *
+ * @param sourceSpec {@link TranslationSpec} for the data to be translated.
+ * @param destSpec {@link TranslationSpec} for the translated data.
+ * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
+ * @param activityId the identifier for the Activity which needs ui translation
+ * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
+ * @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
+ * {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void startTranslation(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
+ @NonNull ActivityId activityId) {
+ // TODO(b/177789967): Return result code or find a way to notify the status.
+ Objects.requireNonNull(sourceSpec);
+ Objects.requireNonNull(destSpec);
+ Objects.requireNonNull(viewIds);
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(activityId.getToken());
+ if (viewIds.size() == 0) {
+ throw new IllegalArgumentException("Invalid empty views: " + viewIds);
+ }
try {
mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec,
- destSpec, viewIds, taskId, mContext.getUserId());
+ destSpec, viewIds, activityId.getToken(), activityId.getTaskId(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -124,14 +160,15 @@ public final class UiTranslationManager {
* Request to disable the ui translation. It will destroy all the {@link Translator}s and no
* longer to show to show the translated text.
*
+ * NOTE: Please use {@code finishTranslation(ActivityId)} instead.
+ *
* @param taskId the Activity Task id which needs ui translation
*/
+ // TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
public void finishTranslation(int taskId) {
try {
- // TODO(b/177394471): The is a temparary API, the expected is finishUiTranslation(
- // Binder). We may need more time to implement it, use task id as initial version.
- mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED,
+ mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
mContext.getUserId());
} catch (RemoteException e) {
@@ -140,17 +177,39 @@ public final class UiTranslationManager {
}
/**
+ * Request to disable the ui translation. It will destroy all the {@link Translator}s and no
+ * longer to show to show the translated text.
+ *
+ * @param activityId the identifier for the Activity which needs ui translation
+ * @throws NullPointerException the activityId or
+ * {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void finishTranslation(@NonNull ActivityId activityId) {
+ try {
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(activityId.getToken());
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request to pause the current ui translation's {@link Translator} which will switch back to
* the original language.
*
+ * NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
+ *
* @param taskId the Activity Task id which needs ui translation
*/
+ // TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
public void pauseTranslation(int taskId) {
try {
- // TODO(b/177394471): The is a temparary API, the expected is pauseUiTranslation(Binder)
- // We may need more time to implement it, use task id as initial version for demo
- mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED,
+ mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
mContext.getUserId());
} catch (RemoteException e) {
@@ -159,21 +218,64 @@ public final class UiTranslationManager {
}
/**
+ * Request to pause the current ui translation's {@link Translator} which will switch back to
+ * the original language.
+ *
+ * @param activityId the identifier for the Activity which needs ui translation
+ * @throws NullPointerException the activityId or
+ * {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void pauseTranslation(@NonNull ActivityId activityId) {
+ try {
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(activityId.getToken());
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request to resume the paused ui translation's {@link Translator} which will switch to the
* translated language if the text had been translated.
*
+ * NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
+ *
* @param taskId the Activity Task id which needs ui translation
*/
+ // TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
public void resumeTranslation(int taskId) {
try {
- // TODO(b/177394471): The is a temparary API, the expected is resumeUiTranslation(
- // Binder). We may need more time to implement it, use task id as initial version.
- mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED,
+ mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
taskId, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Request to resume the paused ui translation's {@link Translator} which will switch to the
+ * translated language if the text had been translated.
+ *
+ * @param activityId the identifier for the Activity which needs ui translation
+ * @throws NullPointerException the activityId or
+ * {@link android.app.assist.ActivityId#getToken()} is {@code null}
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void resumeTranslation(@NonNull ActivityId activityId) {
+ try {
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(activityId.getToken());
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 1b62266c12e2..dc42ad583543 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -29,7 +29,6 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
-import android.graphics.RenderEffect;
import android.graphics.RenderNode;
import android.os.Build;
import android.util.AttributeSet;
@@ -83,6 +82,8 @@ public class EdgeEffect {
public @interface EdgeEffectType {
}
+ private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
+
@SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
@@ -128,6 +129,8 @@ public class EdgeEffect {
private long mStartTime;
private float mDuration;
+ private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
+ private float mStretchDistance = -1f;
private final Interpolator mInterpolator;
@@ -146,6 +149,8 @@ public class EdgeEffect {
private float mPullDistance;
private final Rect mBounds = new Rect();
+ private float mWidth;
+ private float mHeight;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769450)
private final Paint mPaint = new Paint();
private float mRadius;
@@ -202,6 +207,19 @@ public class EdgeEffect {
mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
+
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Configure the distance in pixels to stretch the content. This is only consumed as part
+ * if {@link #setType(int)} is set to {@link #TYPE_STRETCH}
+ * @param stretchDistance Stretch distance in pixels when the target View is overscrolled
+ * @hide
+ */
+ public void setStretchDistance(float stretchDistance) {
+ mStretchDistance = stretchDistance;
}
/**
@@ -437,6 +455,13 @@ public class EdgeEffect {
}
/**
+ * @hide
+ */
+ public void setMaxStretchIntensity(float stretchIntensity) {
+ mStretchIntensity = stretchIntensity;
+ }
+
+ /**
* Set or clear the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
* (content of the render target).
@@ -520,23 +545,55 @@ public class EdgeEffect {
RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
if (mTmpMatrix == null) {
mTmpMatrix = new Matrix();
- mTmpPoints = new float[4];
+ mTmpPoints = new float[12];
}
//noinspection deprecation
recordingCanvas.getMatrix(mTmpMatrix);
- mTmpPoints[0] = mBounds.width() * mDisplacement;
- mTmpPoints[1] = mDistance * mBounds.height();
- mTmpPoints[2] = mTmpPoints[0];
- mTmpPoints[3] = 0;
+
+ mTmpPoints[0] = 0;
+ mTmpPoints[1] = 0; // top-left
+ mTmpPoints[2] = mWidth;
+ mTmpPoints[3] = 0; // top-right
+ mTmpPoints[4] = mWidth;
+ mTmpPoints[5] = mHeight; // bottom-right
+ mTmpPoints[6] = 0;
+ mTmpPoints[7] = mHeight; // bottom-left
+ mTmpPoints[8] = mWidth * mDisplacement;
+ mTmpPoints[9] = 0; // drag start point
+ mTmpPoints[10] = mWidth * mDisplacement;
+ mTmpPoints[11] = mHeight * mDistance; // drag point
mTmpMatrix.mapPoints(mTmpPoints);
- float x = mTmpPoints[0] - mTmpPoints[2];
- float y = mTmpPoints[1] - mTmpPoints[3];
RenderNode renderNode = recordingCanvas.mNode;
- // TODO: use stretchy RenderEffect and use internal API when it is ready
- // TODO: wrap existing RenderEffect
- renderNode.setRenderEffect(RenderEffect.createOffsetEffect(x, y));
+ float left = renderNode.getLeft()
+ + min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
+ float top = renderNode.getTop()
+ + min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
+ float right = renderNode.getLeft()
+ + max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
+ float bottom = renderNode.getTop()
+ + max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
+ // assume rotations of increments of 90 degrees
+ float x = mTmpPoints[10] - mTmpPoints[8];
+ float width = right - left;
+ float vecX = Math.max(-1f, Math.min(1f, x / width));
+ float y = mTmpPoints[11] - mTmpPoints[9];
+ float height = bottom - top;
+ float vecY = Math.max(-1f, Math.min(1f, y / height));
+ renderNode.stretch(
+ left,
+ top,
+ right,
+ bottom,
+ vecX * mStretchIntensity,
+ vecY * mStretchIntensity,
+ // TODO (njawad/mount) figure out proper stretch distance from UX
+ // for now leverage placeholder logic if no stretch distance is provided to
+ // consume the displacement ratio times the minimum of the width or height
+ mStretchDistance > 0 ? mStretchDistance :
+ (mDisplacement * Math.min(mWidth, mHeight))
+ );
}
boolean oneLastFrame = false;
@@ -548,6 +605,18 @@ public class EdgeEffect {
return mState != STATE_IDLE || oneLastFrame;
}
+ private float min(float f1, float f2, float f3, float f4) {
+ float min = Math.min(f1, f2);
+ min = Math.min(min, f3);
+ return Math.min(min, f4);
+ }
+
+ private float max(float f1, float f2, float f3, float f4) {
+ float max = Math.max(f1, f2);
+ max = Math.max(max, f3);
+ return Math.max(max, f4);
+ }
+
/**
* Return the maximum height that the edge effect will be drawn at given the original
* {@link #setSize(int, int) input size}.
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 23915e06335a..bf552e2a501c 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -249,6 +249,26 @@ public class HorizontalScrollView extends FrameLayout {
}
/**
+ * API used for prototyping stretch effect parameters in framework sample apps
+ * @hide
+ */
+ public void setEdgeEffectIntensity(float intensity) {
+ mEdgeGlowLeft.setMaxStretchIntensity(intensity);
+ mEdgeGlowRight.setMaxStretchIntensity(intensity);
+ invalidate();
+ }
+
+ /**
+ * API used for prototyping stretch effect parameters in the framework sample apps
+ * @hide
+ */
+ public void setStretchDistance(float distance) {
+ mEdgeGlowLeft.setStretchDistance(distance);
+ mEdgeGlowRight.setStretchDistance(distance);
+ invalidate();
+ }
+
+ /**
* Sets the right edge effect color.
*
* @param color The color for the right edge effect.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e0b4ec71b0a0..dfb2263a97a7 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -51,7 +51,6 @@ import android.content.res.loader.ResourcesProvider;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Outline;
-import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -76,6 +75,7 @@ import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
+import android.util.SizeF;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
@@ -353,7 +353,7 @@ public class RemoteViews implements Parcelable, Filter {
* Only to be used on children views used in a {@link RemoteViews} with
* {@link RemoteViews#hasSizedRemoteViews()}.
*/
- private PointF mIdealSize = null;
+ private SizeF mIdealSize = null;
@ApplyFlags
private int mApplyFlags = 0;
@@ -705,6 +705,14 @@ public class RemoteViews implements Parcelable, Filter {
public String getPackageName() {
return mContextForResources.getPackageName();
}
+
+ @Override
+ public boolean isRestricted() {
+ // Override isRestricted and direct to resource's implementation. The isRestricted is
+ // used for determining the risky resources loading, e.g. fonts, thus direct to context
+ // for resource.
+ return mContextForResources.isRestricted();
+ }
}
private class SetEmptyView extends Action {
@@ -3034,11 +3042,11 @@ public class RemoteViews implements Parcelable, Filter {
return mSizedRemoteViews != null;
}
- private @Nullable PointF getIdealSize() {
+ private @Nullable SizeF getIdealSize() {
return mIdealSize;
}
- private void setIdealSize(@Nullable PointF size) {
+ private void setIdealSize(@Nullable SizeF size) {
mIdealSize = size;
}
@@ -3086,13 +3094,18 @@ public class RemoteViews implements Parcelable, Filter {
* Create a new RemoteViews object that will inflate the layout with the closest size
* specification.
*
- * The default remote views in that case is always the smallest one provided.
+ * The default remote views in that case is always the one with the smallest area.
+ *
+ * If the {@link RemoteViews} host provides the size of the view, the layout with the largest
+ * area that fits entirely in the provided size will be used (i.e. the width and height of
+ * the layout must be less than the size of the view, with a 1dp margin to account for
+ * rounding). If no layout fits in the view, the layout with the smallest area will be used.
*
* @param remoteViews Mapping of size to layout.
* @throws IllegalArgumentException if the map is empty, there are more than
* MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
*/
- public RemoteViews(@NonNull Map<PointF, RemoteViews> remoteViews) {
+ public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) {
if (remoteViews.isEmpty()) {
throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
}
@@ -3127,8 +3140,8 @@ public class RemoteViews implements Parcelable, Filter {
RemoteViews smallestView = null;
while (remoteViews.hasNext()) {
RemoteViews view = remoteViews.next();
- PointF size = view.getIdealSize();
- float newViewArea = size.x * size.y;
+ SizeF size = view.getIdealSize();
+ float newViewArea = size.getWidth() * size.getHeight();
if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
throw new IllegalArgumentException(
"All RemoteViews must share the same package and user");
@@ -3231,7 +3244,7 @@ public class RemoteViews implements Parcelable, Filter {
if (mode == MODE_NORMAL) {
mApplication = parcel.readInt() == 0 ? info :
ApplicationInfo.CREATOR.createFromParcel(parcel);
- mIdealSize = parcel.readInt() == 0 ? null : PointF.CREATOR.createFromParcel(parcel);
+ mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
mLayoutId = parcel.readInt();
mLightBackgroundLayoutId = parcel.readInt();
@@ -4617,9 +4630,9 @@ public class RemoteViews implements Parcelable, Filter {
*
* This is particularly useful when we only care about the ordering of the distances.
*/
- private static float squareDistance(PointF p1, PointF p2) {
- float dx = p1.x - p2.x;
- float dy = p1.y - p2.y;
+ private static float squareDistance(SizeF p1, SizeF p2) {
+ float dx = p1.getWidth() - p2.getWidth();
+ float dy = p1.getHeight() - p2.getHeight();
return dx * dx + dy * dy;
}
@@ -4629,31 +4642,17 @@ public class RemoteViews implements Parcelable, Filter {
* A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
* are smaller than the ones of the widget, adding some padding to account for rounding errors.
*/
- private static boolean fitsIn(PointF sizeLayout, @Nullable PointF sizeWidget) {
- return sizeWidget != null && (Math.ceil(sizeWidget.x) + 1 > sizeLayout.x)
- && (Math.ceil(sizeWidget.y) + 1 > sizeLayout.y);
+ private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) {
+ return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth())
+ && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight());
}
- /**
- * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
- * size of the widget.
- *
- * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
- * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
- * diagonal the most similar to the widget. If no layout fits or the size of the widget is
- * not specified, the one with the smallest area will be chosen.
- */
- private RemoteViews getRemoteViewsToApply(@NonNull Context context,
- @Nullable PointF widgetSize) {
- if (!hasSizedRemoteViews()) {
- // If there isn't multiple remote views, fall back on the previous methods.
- return getRemoteViewsToApply(context);
- }
+ private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) {
// Find the better remote view
RemoteViews bestFit = null;
float bestSqDist = Float.MAX_VALUE;
for (RemoteViews layout : mSizedRemoteViews) {
- PointF layoutSize = layout.getIdealSize();
+ SizeF layoutSize = layout.getIdealSize();
if (fitsIn(layoutSize, widgetSize)) {
if (bestFit == null) {
bestFit = layout;
@@ -4674,6 +4673,46 @@ public class RemoteViews implements Parcelable, Filter {
return bestFit;
}
+ /**
+ * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
+ * size of the widget.
+ *
+ * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
+ * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
+ * diagonal the most similar to the widget. If no layout fits or the size of the widget is
+ * not specified, the one with the smallest area will be chosen.
+ *
+ * @hide
+ */
+ public RemoteViews getRemoteViewsToApply(@NonNull Context context,
+ @Nullable SizeF widgetSize) {
+ if (!hasSizedRemoteViews()) {
+ // If there isn't multiple remote views, fall back on the previous methods.
+ return getRemoteViewsToApply(context);
+ }
+ return findBestFitLayout(widgetSize);
+ }
+
+ /**
+ * Checks whether the change of size will lead to using a different {@link RemoteViews}.
+ *
+ * @hide
+ */
+ @Nullable
+ public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize,
+ @NonNull SizeF newSize) {
+ if (!hasSizedRemoteViews()) {
+ return null;
+ }
+ RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout(
+ oldSize);
+ RemoteViews newBestFit = findBestFitLayout(newSize);
+ if (oldBestFit != newBestFit) {
+ return newBestFit;
+ }
+ return null;
+ }
+
/**
* Inflates the view hierarchy represented by this object and applies
@@ -4697,7 +4736,7 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public View apply(@NonNull Context context, @NonNull ViewGroup parent,
- @Nullable InteractionHandler handler, @Nullable PointF size) {
+ @Nullable InteractionHandler handler, @Nullable SizeF size) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent);
@@ -4714,7 +4753,7 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
- @Nullable PointF size) {
+ @Nullable SizeF size) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
@@ -4724,7 +4763,7 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public View apply(Context context, ViewGroup parent, InteractionHandler handler,
- @NonNull PointF size, @Nullable ColorResources colorResources) {
+ @NonNull SizeF size, @Nullable ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
View result = inflateView(context, rvToApply, parent, 0, colorResources);
@@ -4820,21 +4859,21 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
- PointF size) {
+ SizeF size) {
return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
.startTaskOnExecutor(executor);
}
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
- OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
.startTaskOnExecutor(executor);
}
private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
handler, colorResources, null /* result */);
@@ -4960,7 +4999,7 @@ public class RemoteViews implements Parcelable, Filter {
}
/** @hide */
- public void reapply(Context context, View v, InteractionHandler handler, PointF size,
+ public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
@@ -5004,7 +5043,7 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
- OnViewAppliedListener listener, InteractionHandler handler, PointF size,
+ OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da79afe0..30067296f967 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -281,6 +281,26 @@ public class ScrollView extends FrameLayout {
}
/**
+ * API used for prototyping stretch effect parameters in framework sample apps
+ * @hide
+ */
+ public void setEdgeEffectIntensity(float intensity) {
+ mEdgeGlowTop.setMaxStretchIntensity(intensity);
+ mEdgeGlowBottom.setMaxStretchIntensity(intensity);
+ invalidate();
+ }
+
+ /**
+ * API used for prototyping stretch effect parameters in the framework sample apps
+ * @hide
+ */
+ public void setStretchDistance(float distance) {
+ mEdgeGlowTop.setStretchDistance(distance);
+ mEdgeGlowBottom.setStretchDistance(distance);
+ invalidate();
+ }
+
+ /**
* Sets the bottom edge effect color.
*
* @param color The color for the bottom edge effect.
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/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 63b9e9befb77..d1c1e40d1578 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -35,6 +35,31 @@ import android.view.WindowManager;
@TestApi
public final class StartingWindowInfo implements Parcelable {
/**
+ * Prefer nothing or not care the type of starting window.
+ * @hide
+ */
+ public static final int STARTING_WINDOW_TYPE_NONE = 0;
+ /**
+ * Prefer splash screen starting window.
+ * @hide
+ */
+ public static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 1;
+ /**
+ * Prefer snapshot starting window.
+ * @hide
+ */
+ public static final int STARTING_WINDOW_TYPE_SNAPSHOT = 2;
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = "STARTING_WINDOW_TYPE_", value = {
+ STARTING_WINDOW_TYPE_NONE,
+ STARTING_WINDOW_TYPE_SPLASH_SCREEN,
+ STARTING_WINDOW_TYPE_SNAPSHOT
+ })
+ public @interface StartingWindowType {}
+
+ /**
* The {@link TaskInfo} from this task.
* @hide
*/
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index dc07e44d4d98..f1e5fb95ea54 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -46,7 +46,7 @@ public class TaskSnapshot implements Parcelable {
private final int mOrientation;
/** See {@link android.view.Surface.Rotation} */
@Surface.Rotation
- private int mRotation;
+ private final int mRotation;
/** The size of the snapshot before scaling */
private final Point mTaskSize;
private final Rect mContentInsets;
@@ -90,15 +90,15 @@ public class TaskSnapshot implements Parcelable {
private TaskSnapshot(Parcel source) {
mId = source.readLong();
mTopActivityComponent = ComponentName.readFromParcel(source);
- mSnapshot = source.readParcelable(null /* classLoader */);
+ mSnapshot = source.readTypedObject(HardwareBuffer.CREATOR);
int colorSpaceId = source.readInt();
mColorSpace = colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length
? ColorSpace.get(ColorSpace.Named.values()[colorSpaceId])
: ColorSpace.get(ColorSpace.Named.SRGB);
mOrientation = source.readInt();
mRotation = source.readInt();
- mTaskSize = source.readParcelable(null /* classLoader */);
- mContentInsets = source.readParcelable(null /* classLoader */);
+ mTaskSize = source.readTypedObject(Point.CREATOR);
+ mContentInsets = source.readTypedObject(Rect.CREATOR);
mIsLowResolution = source.readBoolean();
mIsRealSnapshot = source.readBoolean();
mWindowingMode = source.readInt();
@@ -235,13 +235,12 @@ public class TaskSnapshot implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mId);
ComponentName.writeToParcel(mTopActivityComponent, dest);
- dest.writeParcelable(mSnapshot != null && !mSnapshot.isClosed() ? mSnapshot : null,
- 0);
+ dest.writeTypedObject(mSnapshot != null && !mSnapshot.isClosed() ? mSnapshot : null, 0);
dest.writeInt(mColorSpace.getId());
dest.writeInt(mOrientation);
dest.writeInt(mRotation);
- dest.writeParcelable(mTaskSize, 0);
- dest.writeParcelable(mContentInsets, 0);
+ dest.writeTypedObject(mTaskSize, 0);
+ dest.writeTypedObject(mContentInsets, 0);
dest.writeBoolean(mIsLowResolution);
dest.writeBoolean(mIsRealSnapshot);
dest.writeInt(mWindowingMode);
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 4421f06460a0..141f47b130d1 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -139,8 +139,8 @@ public final class TransitionFilter implements Parcelable {
boolean matches(@NonNull TransitionInfo info) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getParent() != null) {
- // Only look at the top animating windows.
+ if (!TransitionInfo.isIndependent(change, info)) {
+ // Only look at independent animating windows.
continue;
}
if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index d1d49b6d4722..499ce25f8bb9 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -246,6 +246,33 @@ public final class TransitionInfo implements Parcelable {
return sb.toString();
}
+ /**
+ * Indication that `change` is independent of parents (ie. it has a different type of
+ * transition vs. "going along for the ride")
+ */
+ public static boolean isIndependent(TransitionInfo.Change change, TransitionInfo info) {
+ // If the change has no parent (it is root), then it is independent
+ if (change.getParent() == null) return true;
+
+ // non-visibility changes will just be folded into the parent change, so they aren't
+ // independent either.
+ if (change.getMode() == TRANSIT_CHANGE) return false;
+
+ TransitionInfo.Change parentChg = info.getChange(change.getParent());
+ while (parentChg != null) {
+ // If the parent is a visibility change, it will include the results of all child
+ // changes into itself, so none of its children can be independent.
+ if (parentChg.getMode() != TRANSIT_CHANGE) return false;
+
+ // If there are no more parents left, then all the parents, so far, have not been
+ // visibility changes which means this change is indpendent.
+ if (parentChg.getParent() == null) return true;
+
+ parentChg = info.getChange(parentChg.getParent());
+ }
+ return false;
+ }
+
/** Represents the change a WindowContainer undergoes during a transition */
public static final class Change implements Parcelable {
private final WindowContainerToken mContainer;
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/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
index 01e45f613986..66206bf8297a 100644
--- a/core/java/com/android/internal/graphics/palette/WuQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java
@@ -16,7 +16,6 @@
package com.android.internal.graphics.palette;
-
import java.util.ArrayList;
import java.util.List;
@@ -120,7 +119,11 @@ public class WuQuantizer implements Quantizer {
}
}
- for (k = 0; k < mMaxColors; ++k) {
+ // If extraction is run on a set of pixels whose count is less than the
+ // number of max colors, then colors.length < max colors, and accesses
+ // to colors[index] inside the for loop throw an ArrayOutOfBoundsException.
+ int numColorsToCreate = (int) Math.min(mMaxColors, colors.length);
+ for (k = 0; k < numColorsToCreate; ++k) {
weight = getVolume(cube[k], mWt);
if (weight > 0) {
red = (int) (getVolume(cube[k], mMr) / weight);
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/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index 21131738cede..3958b9eefefb 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -225,4 +225,31 @@ public final class CallbackUtils {
callback.onResult();
} catch (RemoteException ignored) { }
}
+
+ /**
+ * A utility method using given {@link IIInputContentUriTokenResultCallback} to callback the
+ * result.
+ *
+ * @param callback {@link IIInputContentUriTokenResultCallback} to be called back.
+ * @param resultSupplier the supplier from which the result is provided.
+ */
+ public static void onResult(@NonNull IIInputContentUriTokenResultCallback callback,
+ @NonNull Supplier<IInputContentUriToken> resultSupplier) {
+ IInputContentUriToken result = null;
+ Throwable exception = null;
+
+ try {
+ result = resultSupplier.get();
+ } catch (Throwable throwable) {
+ exception = throwable;
+ }
+
+ try {
+ if (exception != null) {
+ callback.onError(ThrowableHolder.of(exception));
+ return;
+ }
+ callback.onResult(result);
+ } catch (RemoteException ignored) { }
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index d6a466316ed4..ba3a34334e86 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -444,6 +444,13 @@ public final class Completable {
}
/**
+ * @return an instance of {@link Completable.IInputContentUriToken}.
+ */
+ public static Completable.IInputContentUriToken createIInputContentUriToken() {
+ return new Completable.IInputContentUriToken();
+ }
+
+ /**
* @return an instance of {@link Completable.Void}.
*/
public static Completable.Void createVoid() {
@@ -497,6 +504,12 @@ public final class Completable {
extends Values<List<android.view.inputmethod.InputMethodInfo>> { }
/**
+ * Completable object of {@link IInputContentUriToken>}.
+ */
+ public static final class IInputContentUriToken
+ extends Values<com.android.internal.inputmethod.IInputContentUriToken> { }
+
+ /**
* Await the result by the {@link Completable.Values}.
*
* @return the result once {@link ValueBase#onComplete()}.
diff --git a/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl b/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
new file mode 100644
index 000000000000..2e6d2247b51b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.ThrowableHolder;
+
+oneway interface IIInputContentUriTokenResultCallback {
+ void onResult(in IInputContentUriToken result);
+ void onError(in ThrowableHolder exception);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index f0e26cf4bbcf..e4dd7b0629b5 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -19,25 +19,31 @@ package com.android.internal.inputmethod;
import android.net.Uri;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.inputmethod.IBooleanResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
+import com.android.internal.inputmethod.IVoidResultCallback;
/**
* Defines priviledged operations that only the current IME is allowed to call.
* Actual operations are implemented and handled by InputMethodManagerService.
*/
-interface IInputMethodPrivilegedOperations {
- void setImeWindowStatus(int vis, int backDisposition);
- void reportStartInput(in IBinder startInputToken);
- IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName);
- void reportFullscreenMode(boolean fullscreen);
- void setInputMethod(String id);
- void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype);
- void hideMySoftInput(int flags);
- void showMySoftInput(int flags);
- void updateStatusIcon(String packageName, int iconId);
- boolean switchToPreviousInputMethod();
- boolean switchToNextInputMethod(boolean onlyCurrentIme);
- boolean shouldOfferSwitchingToNextInputMethod();
- void notifyUserAction();
- void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible);
+oneway interface IInputMethodPrivilegedOperations {
+ void setImeWindowStatus(int vis, int backDisposition, in IVoidResultCallback resultCallback);
+ void reportStartInput(in IBinder startInputToken, in IVoidResultCallback resultCallback);
+ void createInputContentUriToken(in Uri contentUri, in String packageName,
+ in IIInputContentUriTokenResultCallback resultCallback);
+ void reportFullscreenMode(boolean fullscreen, in IVoidResultCallback resultCallback);
+ void setInputMethod(String id, in IVoidResultCallback resultCallback);
+ void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
+ in IVoidResultCallback resultCallback);
+ void hideMySoftInput(int flags, in IVoidResultCallback resultCallback);
+ void showMySoftInput(int flags, in IVoidResultCallback resultCallback);
+ void updateStatusIcon(String packageName, int iconId, in IVoidResultCallback resultCallback);
+ void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
+ void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
+ void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
+ void notifyUserAction(in IVoidResultCallback resultCallback);
+ void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
+ in IVoidResultCallback resultCallback);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index c353de88dc3b..93374ba0cf46 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -228,6 +228,8 @@ public final class InputMethodDebug {
return "HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR";
case SoftInputShowHideReason.HIDE_REMOVE_CLIENT:
return "HIDE_REMOVE_CLIENT";
+ case SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY:
+ return "SHOW_RESTORE_IME_VISIBILITY";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index d6730e8b0bdb..04cf3f3e546f 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -95,7 +95,8 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int)}.
+ * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int,
+ * IVoidResultCallback)}.
*
* @param vis visibility flags
* @param backDisposition disposition flags
@@ -112,14 +113,17 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.setImeWindowStatus(vis, backDisposition);
+ final Completable.Void value = Completable.createVoid();
+ ops.setImeWindowStatus(vis, backDisposition, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder)}.
+ * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder,
+ * IVoidResultCallback)}.
*
* @param startInputToken {@link IBinder} token to distinguish startInput session
*/
@@ -130,14 +134,17 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.reportStartInput(startInputToken);
+ final Completable.Void value = Completable.createVoid();
+ ops.reportStartInput(startInputToken, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}.
+ * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
+ * IIInputContentUriTokenResultCallback)}.
*
* @param contentUri Content URI to which a temporary read permission should be granted
* @param packageName Indicates what package needs to have a temporary read permission
@@ -151,7 +158,10 @@ public final class InputMethodPrivilegedOperations {
return null;
}
try {
- return ops.createInputContentUriToken(contentUri, packageName);
+ final Completable.IInputContentUriToken value =
+ Completable.createIInputContentUriToken();
+ ops.createInputContentUriToken(contentUri, packageName, ResultCallbacks.of(value));
+ return Completable.getResult(value);
} catch (RemoteException e) {
// For historical reasons, this error was silently ignored.
// Note that the caller already logs error so we do not need additional Log.e() here.
@@ -161,7 +171,8 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean)}.
+ * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean,
+ * IVoidResultCallback)}.
*
* @param fullscreen {@code true} if the IME enters full screen mode
*/
@@ -172,14 +183,17 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.reportFullscreenMode(fullscreen);
+ final Completable.Void value = Completable.createVoid();
+ ops.reportFullscreenMode(fullscreen, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int)}.
+ * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int,
+ * IVoidResultCallback)}.
*
* @param packageName package name from which the status icon should be loaded
* @param iconResId resource ID of the icon to be loaded
@@ -191,14 +205,16 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.updateStatusIcon(packageName, iconResId);
+ final Completable.Void value = Completable.createVoid();
+ ops.updateStatusIcon(packageName, iconResId, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String)}.
+ * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String, IVoidResultCallback)}.
*
* @param id IME ID of the IME to switch to
* @see android.view.inputmethod.InputMethodInfo#getId()
@@ -210,7 +226,9 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.setInputMethod(id);
+ final Completable.Void value = Completable.createVoid();
+ ops.setInputMethod(id, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -218,7 +236,7 @@ public final class InputMethodPrivilegedOperations {
/**
* Calls {@link IInputMethodPrivilegedOperations#setInputMethodAndSubtype(String,
- * InputMethodSubtype)}
+ * InputMethodSubtype, IVoidResultCallback)}
*
* @param id IME ID of the IME to switch to
* @param subtype {@link InputMethodSubtype} to switch to
@@ -231,14 +249,16 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.setInputMethodAndSubtype(id, subtype);
+ final Completable.Void value = Completable.createVoid();
+ ops.setInputMethodAndSubtype(id, subtype, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int)}
+ * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, IVoidResultCallback)}
*
* @param flags additional operating flags
* @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
@@ -251,14 +271,16 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.hideMySoftInput(flags);
+ final Completable.Void value = Completable.createVoid();
+ ops.hideMySoftInput(flags, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int)}
+ * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, IVoidResultCallback)}
*
* @param flags additional operating flags
* @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
@@ -271,14 +293,17 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.showMySoftInput(flags);
+ final Completable.Void value = Completable.createVoid();
+ ops.showMySoftInput(flags, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod()}
+ * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod(
+ * IBooleanResultCallback)}
*
* @return {@code true} if handled
*/
@@ -289,14 +314,17 @@ public final class InputMethodPrivilegedOperations {
return false;
}
try {
- return ops.switchToPreviousInputMethod();
+ final Completable.Boolean value = Completable.createBoolean();
+ ops.switchToPreviousInputMethod(ResultCallbacks.of(value));
+ return Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean)}
+ * Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean,
+ * IBooleanResultCallback)}
*
* @param onlyCurrentIme {@code true} to switch to a {@link InputMethodSubtype} within the same
* IME
@@ -309,14 +337,17 @@ public final class InputMethodPrivilegedOperations {
return false;
}
try {
- return ops.switchToNextInputMethod(onlyCurrentIme);
+ final Completable.Boolean value = Completable.createBoolean();
+ ops.switchToNextInputMethod(onlyCurrentIme, ResultCallbacks.of(value));
+ return Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod()}
+ * Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod(
+ * IBooleanResultCallback)}
*
* @return {@code true} if the IEM should offer a way to globally switch IME
*/
@@ -327,14 +358,16 @@ public final class InputMethodPrivilegedOperations {
return false;
}
try {
- return ops.shouldOfferSwitchingToNextInputMethod();
+ final Completable.Boolean value = Completable.createBoolean();
+ ops.shouldOfferSwitchingToNextInputMethod(ResultCallbacks.of(value));
+ return Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction()}
+ * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
*/
@AnyThread
public void notifyUserAction() {
@@ -343,14 +376,17 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.notifyUserAction();
+ final Completable.Void value = Completable.createVoid();
+ ops.notifyUserAction(ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}.
+ * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
+ * IVoidResultCallback)}.
*
* @param showOrHideInputToken placeholder token that maps to window requesting
* {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -365,7 +401,9 @@ public final class InputMethodPrivilegedOperations {
return;
}
try {
- ops.applyImeVisibility(showOrHideInputToken, setVisible);
+ final Completable.Void value = Completable.createVoid();
+ ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 2a48c1f60aa9..c56ed2d19927 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -387,4 +387,41 @@ public final class ResultCallbacks {
}
};
}
+
+ /**
+ * Creates {@link IIInputContentUriTokenResultCallback.Stub} that is to set
+ * {@link Completable.IInputContentUriToken} when receiving the result.
+ *
+ * @param value {@link Completable.IInputContentUriToken} to be set when receiving the result.
+ * @return {@link IIInputContentUriTokenResultCallback.Stub} that can be passed as a binder IPC
+ * parameter.
+ */
+ @AnyThread
+ public static IIInputContentUriTokenResultCallback.Stub of(
+ @NonNull Completable.IInputContentUriToken value) {
+ final AtomicReference<WeakReference<Completable.IInputContentUriToken>>
+ atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+ return new IIInputContentUriTokenResultCallback.Stub() {
+ @BinderThread
+ @Override
+ public void onResult(IInputContentUriToken result) {
+ final Completable.IInputContentUriToken value = unwrap(atomicRef);
+ if (value == null) {
+ return;
+ }
+ value.onComplete(result);
+ }
+
+ @BinderThread
+ @Override
+ public void onError(ThrowableHolder throwableHolder) {
+ final Completable.IInputContentUriToken value = unwrap(atomicRef);
+ if (value == null) {
+ return;
+ }
+ value.onError(throwableHolder);
+ }
+ };
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index 1553e2eb0793..f1cdf2b38c4c 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -49,7 +49,8 @@ import java.lang.annotation.Retention;
SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
SoftInputShowHideReason.HIDE_BUBBLES,
SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR,
- SoftInputShowHideReason.HIDE_REMOVE_CLIENT})
+ SoftInputShowHideReason.HIDE_REMOVE_CLIENT,
+ SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY})
public @interface SoftInputShowHideReason {
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
int SHOW_SOFT_INPUT = 0;
@@ -167,4 +168,10 @@ public @interface SoftInputShowHideReason {
* Hide soft input when a {@link com.android.internal.view.IInputMethodClient} is removed.
*/
int HIDE_REMOVE_CLIENT = 21;
+
+ /**
+ * Show soft input when the system invoking
+ * {@link com.android.server.wm.WindowManagerInternal#shouldRestoreImeVisibility}.
+ */
+ int SHOW_RESTORE_IME_VISIBILITY = 22;
}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 342456a58091..db0b48e130a3 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
@@ -324,11 +327,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
if (info.surfaceControlCallbackFired) {
totalFramesCount++;
-
- // Only count missed frames if it's not stuffed.
if ((info.jankType & PREDICTION_ERROR) != 0
- || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0
- && (info.jankType & BUFFER_STUFFING) == 0)) {
+ || ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
Log.w(TAG, "Missed App frame:" + info.jankType);
missedAppFramesCount++;
}
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/net/NetworkUtilsInternal.java b/core/java/com/android/internal/net/NetworkUtilsInternal.java
index 571d7e721094..052959abff69 100644
--- a/core/java/com/android/internal/net/NetworkUtilsInternal.java
+++ b/core/java/com/android/internal/net/NetworkUtilsInternal.java
@@ -22,6 +22,8 @@ import static android.system.OsConstants.AF_INET6;
import android.annotation.NonNull;
import android.system.Os;
+import java.io.FileDescriptor;
+
/** @hide */
public class NetworkUtilsInternal {
@@ -36,6 +38,20 @@ public class NetworkUtilsInternal {
public static native void setAllowNetworkingForProcess(boolean allowNetworking);
/**
+ * Protect {@code fd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public static native boolean protectFromVpn(FileDescriptor fd);
+
+ /**
+ * Protect {@code socketfd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public static native boolean protectFromVpn(int socketfd);
+
+ /**
* Returns true if the hostname is weakly validated.
* @param hostname Name of host to validate.
* @return True if it's a valid-ish hostname.
diff --git a/core/java/com/android/internal/os/DischargedPowerCalculator.java b/core/java/com/android/internal/os/BatteryChargeCalculator.java
index e94020cc4f18..dc72f3267390 100644
--- a/core/java/com/android/internal/os/DischargedPowerCalculator.java
+++ b/core/java/com/android/internal/os/BatteryChargeCalculator.java
@@ -27,10 +27,10 @@ import java.util.List;
/**
* Estimates the battery discharge amounts.
*/
-public class DischargedPowerCalculator extends PowerCalculator {
+public class BatteryChargeCalculator extends PowerCalculator {
private final double mBatteryCapacity;
- public DischargedPowerCalculator(PowerProfile powerProfile) {
+ public BatteryChargeCalculator(PowerProfile powerProfile) {
mBatteryCapacity = powerProfile.getBatteryCapacity();
}
@@ -42,6 +42,16 @@ public class DischargedPowerCalculator extends PowerCalculator {
.setDischargedPowerRange(
batteryStats.getLowDischargeAmountSinceCharge() * mBatteryCapacity / 100,
batteryStats.getHighDischargeAmountSinceCharge() * mBatteryCapacity / 100);
+
+ final long batteryTimeRemainingMs = batteryStats.computeBatteryTimeRemaining(rawRealtimeUs);
+ if (batteryTimeRemainingMs != -1) {
+ builder.setBatteryTimeRemainingMs(batteryTimeRemainingMs / 1000);
+ }
+
+ final long chargeTimeRemainingMs = batteryStats.computeChargeTimeRemaining(rawRealtimeUs);
+ if (chargeTimeRemainingMs != -1) {
+ builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
+ }
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 15b584d1fd06..619cd8e2fc27 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -55,7 +55,7 @@ public class BatteryUsageStatsProvider {
mPowerCalculators = new ArrayList<>();
// Power calculators are applied in the order of registration
- mPowerCalculators.add(new DischargedPowerCalculator(mPowerProfile));
+ mPowerCalculators.add(new BatteryChargeCalculator(mPowerProfile));
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 0aa54f556b92..3f01ebb1afe7 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -6,6 +6,7 @@ per-file *Cpu* = file:CPU_OWNERS
per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
+per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 6b1d408bee9a..8b5a62ada1c5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -202,7 +202,7 @@ public final class Zygote {
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
/** List of allowlisted packages and its app data info: volume uuid and inode. */
- public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+ public static final String ALLOWLISTED_DATA_INFO_MAP = "--allowlisted-data-info-map";
/** Bind mount app storage dirs to lower fs not via fuse */
public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
@@ -324,7 +324,7 @@ public final class Zygote {
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
- * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
+ * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
@@ -334,14 +334,14 @@ public final class Zygote {
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList,
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+ pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs,
bindMountAppStorageDirs);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
@@ -364,7 +364,7 @@ public final class Zygote {
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
- String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
+ String[] allowlistedDataInfoList, boolean bindMountAppDataDirs,
boolean bindMountAppStorageDirs);
/**
@@ -392,18 +392,18 @@ public final class Zygote {
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
- * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
+ * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ String[] pkgDataInfoList, String[] allowlistedDataInfoList,
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, whitelistedDataInfoList,
+ pkgDataInfoList, allowlistedDataInfoList,
bindMountAppDataDirs, bindMountAppStorageDirs);
// Note that this event ends at the end of handleChildProc.
@@ -428,7 +428,7 @@ public final class Zygote {
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ String[] pkgDataInfoList, String[] allowlistedDataInfoList,
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
/**
@@ -807,7 +807,7 @@ public final class Zygote {
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
- args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+ args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 65b454d47db2..ef8398294c5b 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -230,7 +230,7 @@ class ZygoteArguments {
* A list that stores all allowlisted app data info: volume uuid and inode.
* Null if it does need to do app data isolation.
*/
- String[] mWhitelistedDataInfoList;
+ String[] mAllowlistedDataInfoList;
/**
* @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
@@ -475,8 +475,8 @@ class ZygoteArguments {
}
} else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
mPkgDataInfoList = getAssignmentList(arg);
- } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
- mWhitelistedDataInfoList = getAssignmentList(arg);
+ } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) {
+ mAllowlistedDataInfoList = getAssignmentList(arg);
} else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
mBindMountAppStorageDirs = true;
} else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 37c75907061c..1673362028f9 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -265,7 +265,7 @@ class ZygoteConnection {
fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
- parsedArgs.mWhitelistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
+ parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
parsedArgs.mBindMountAppStorageDirs);
try {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 9840013935f8..9a91d2028953 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -124,6 +124,7 @@ import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.FloatingToolbar;
import java.util.List;
+import java.util.function.Consumer;
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -282,14 +283,19 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private final Paint mLegacyNavigationBarBackgroundPaint = new Paint();
private Insets mBackgroundInsets = Insets.NONE;
private Insets mLastBackgroundInsets = Insets.NONE;
- private int mLastBackgroundBlurRadius = 0;
private boolean mDrawLegacyNavigationBarBackground;
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
+
+ private int mOriginalBackgroundBlurRadius = 0;
+ private int mBackgroundBlurRadius = 0;
+ private int mLastBackgroundBlurRadius = 0;
+ private boolean mCrossWindowBlurEnabled;
private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> {
- updateBackgroundBlur();
+ updateBackgroundBlurCorners();
return true;
};
+ private Consumer<Boolean> mCrossWindowBlurEnabledListener;
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
@@ -1272,23 +1278,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
if (mBackgroundInsets.equals(mLastBackgroundInsets)
- && mWindow.mBackgroundBlurRadius == mLastBackgroundBlurRadius
+ && mBackgroundBlurRadius == mLastBackgroundBlurRadius
&& mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
return;
}
Drawable destDrawable = mOriginalBackgroundDrawable;
- if (mWindow.mBackgroundBlurRadius > 0 && getViewRootImpl() != null
- && mWindow.isTranslucent()) {
- if (mBackgroundBlurDrawable == null) {
- mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();
- }
+ if (mBackgroundBlurRadius > 0) {
destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable,
mOriginalBackgroundDrawable});
- mLastBackgroundBlurRadius = mWindow.mBackgroundBlurRadius;
}
-
if (destDrawable != null && !mBackgroundInsets.equals(Insets.NONE)) {
destDrawable = new InsetDrawable(destDrawable,
mBackgroundInsets.left, mBackgroundInsets.top,
@@ -1309,23 +1309,60 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
super.setBackgroundDrawable(destDrawable);
mLastBackgroundInsets = mBackgroundInsets;
+ mLastBackgroundBlurRadius = mBackgroundBlurRadius;
mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
}
- private void updateBackgroundBlur() {
+ private void updateBackgroundBlurCorners() {
if (mBackgroundBlurDrawable == null) return;
+ float cornerRadius = 0;
// If the blur radius is 0, the blur region won't be sent to surface flinger, so we don't
// need to calculate the corner radius.
- if (mWindow.mBackgroundBlurRadius > 0) {
- if (mOriginalBackgroundDrawable != null) {
- final Outline outline = new Outline();
- mOriginalBackgroundDrawable.getOutline(outline);
- mBackgroundBlurDrawable.setCornerRadius(outline.mMode == Outline.MODE_ROUND_RECT
- ? outline.getRadius() : 0);
+ if (mBackgroundBlurRadius != 0 && mOriginalBackgroundDrawable != null) {
+ final Outline outline = new Outline();
+ mOriginalBackgroundDrawable.getOutline(outline);
+ cornerRadius = outline.mMode == Outline.MODE_ROUND_RECT ? outline.getRadius() : 0;
+ }
+ mBackgroundBlurDrawable.setCornerRadius(cornerRadius);
+ }
+
+ private void updateBackgroundBlurRadius() {
+ if (getViewRootImpl() == null) return;
+
+ mBackgroundBlurRadius = mCrossWindowBlurEnabled && mWindow.isTranslucent()
+ ? mOriginalBackgroundBlurRadius : 0;
+ if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) {
+ mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();
+ }
+
+ if (mBackgroundBlurDrawable != null) {
+ mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius);
+ updateBackgroundDrawable();
+ }
+ }
+
+ void setBackgroundBlurRadius(int blurRadius) {
+ mOriginalBackgroundBlurRadius = blurRadius;
+ if (blurRadius > 0) {
+ if (mCrossWindowBlurEnabledListener == null) {
+ mCrossWindowBlurEnabledListener = enabled -> {
+ mCrossWindowBlurEnabled = enabled;
+ updateBackgroundBlurRadius();
+ };
+ getContext().getSystemService(WindowManager.class)
+ .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
+ getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
+ } else {
+ updateBackgroundBlurRadius();
}
+ } else if (mCrossWindowBlurEnabledListener != null) {
+ mCrossWindowBlurEnabledListener = null;
+ getContext().getSystemService(WindowManager.class)
+ .removeCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
+ getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
+ updateBackgroundBlurRadius();
}
- mBackgroundBlurDrawable.setBlurRadius(mWindow.mBackgroundBlurRadius);
}
@Override
@@ -1758,9 +1795,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
cb.onAttachedToWindow();
}
- getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
- updateBackgroundDrawable();
-
if (mFeatureId == -1) {
/*
* The main window has been attached, try to restore any panels
@@ -1782,6 +1816,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// renderer about it.
mBackdropFrameRenderer.onConfigurationChange();
}
+
+ updateBackgroundBlurRadius();
+
mWindow.onViewRootImplSet(getViewRootImpl());
}
@@ -1794,8 +1831,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
cb.onDetachedFromWindow();
}
- getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
-
if (mWindow.mDecorContentParent != null) {
mWindow.mDecorContentParent.dismissPopups();
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index d06413c3ca15..6049486b380c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -75,6 +75,7 @@ import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
import android.view.IScrollCaptureCallbacks;
@@ -258,7 +259,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
Drawable mBackgroundDrawable = null;
Drawable mBackgroundFallbackDrawable = null;
- int mBackgroundBlurRadius = 0;
+ private int mBackgroundBlurRadius = 0;
private boolean mLoadElevation = true;
private float mElevation;
@@ -1527,9 +1528,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public final void setBackgroundBlurRadius(int blurRadius) {
super.setBackgroundBlurRadius(blurRadius);
- if (getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_CROSS_LAYER_BLUR)) {
- mBackgroundBlurRadius = Math.max(blurRadius, 0);
+ if (CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED) {
+ if (mBackgroundBlurRadius != Math.max(blurRadius, 0)) {
+ mBackgroundBlurRadius = Math.max(blurRadius, 0);
+ mDecor.setBackgroundBlurRadius(mBackgroundBlurRadius);
+ }
}
}
@@ -2556,8 +2559,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
}
- params.blurBehindRadius = a.getDimensionPixelSize(
- android.R.styleable.Window_windowBlurBehindRadius, 0);
+ params.setBlurBehindRadius(a.getDimensionPixelSize(
+ android.R.styleable.Window_windowBlurBehindRadius, 0));
}
setBackgroundBlurRadius(a.getDimensionPixelSize(
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/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b40ffb0136f2..bac6bbe43c91 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -95,9 +95,6 @@ public class SystemConfig {
// property for runtime configuration differentiation in vendor
private static final String VENDOR_SKU_PROPERTY = "ro.boot.product.vendor.sku";
- // property for background blur support in surface flinger
- private static final String BLUR_PROPERTY = "ro.surface_flinger.supports_background_blur";
-
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
int[] mGlobalGids = EmptyArray.INT;
@@ -1237,8 +1234,7 @@ public class SystemConfig {
final int incrementalVersion = IncrementalManager.getVersion();
if (incrementalVersion > 0) {
- addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
- addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, incrementalVersion);
+ addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, incrementalVersion);
}
if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) {
@@ -1249,10 +1245,6 @@ public class SystemConfig {
addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
}
- if (SystemProperties.get(BLUR_PROPERTY, "default").equals("1")) {
- addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0);
- }
-
if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2287900795a5..d6d33873adaa 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -51,6 +51,7 @@ cc_library_shared {
"android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
+ ":deviceproductinfoconstants_aidl",
],
include_dirs: [
@@ -150,7 +151,7 @@ cc_library_shared {
"android_os_VintfRuntimeInfo.cpp",
"android_os_incremental_IncrementalManager.cpp",
"android_net_LocalSocketImpl.cpp",
- "android_net_NetUtils.cpp",
+ "android_net_NetworkUtils.cpp",
"android_service_DataLoaderService.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 35d1d7bd7946..f6629fd250f6 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -43,6 +43,7 @@ per-file android_os_HwRemoteBinder* = file:platform/system/libhwbinder:/OWNERS
per-file EphemeralStorage* = file:platform/system/libhwbinder:/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file fd_utils.* = file:/ZYGOTE_OWNERS
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file android_animation_* = file:/core/java/android/animation/OWNERS
per-file android_app_admin_* = file:/core/java/android/app/admin/OWNERS
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index c7439f1b32d4..b0c575162b56 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -83,6 +83,10 @@ class LoaderAssetsProvider : public AssetsProvider {
return true;
}
+ std::optional<std::string_view> GetPath() const override {
+ return {};
+ }
+
const std::string& GetDebugName() const override {
return debug_name_;
}
@@ -358,8 +362,16 @@ static jlong NativeGetFinalizer(JNIEnv* /*env*/, jclass /*clazz*/) {
}
static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return env->NewStringUTF(apk_assets->GetPath().c_str());
+ auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ if (auto path = apk_assets->GetPath()) {
+ return env->NewStringUTF(path->data());
+ }
+ return nullptr;
+}
+
+static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ return env->NewStringUTF(apk_assets->GetDebugName().c_str());
}
static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
@@ -467,6 +479,7 @@ static const JNINativeMethod gApkAssetsMethods[] = {
(void*)NativeLoadFromFdOffset},
{"nativeGetFinalizer", "()J", (void*)NativeGetFinalizer},
{"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
+ {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
{"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
{"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetworkUtils.cpp
index e2af87ee1adf..750810840bde 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetworkUtils.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "NetUtils"
+#define LOG_TAG "NetworkUtils"
#include <vector>
@@ -123,15 +123,6 @@ static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, job
return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
-static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
-{
- return (jboolean) !protectFromVpn(socket);
-}
-
-static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
- return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
-}
-
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -276,8 +267,6 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
- { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
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/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 451ea93349f7..cbf4481bd2f1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -27,6 +27,7 @@
#include <android-base/chrono_utils.h>
#include <android/graphics/region.h>
#include <android/gui/BnScreenCaptureListener.h>
+#include <android/hardware/display/IDeviceProductInfoConstants.h>
#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
@@ -1022,16 +1023,24 @@ static jobject convertDeviceProductInfoToJavaObject(
} else {
LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
}
- auto relativeAddress = env->NewIntArray(info->relativeAddress.size());
- auto relativeAddressData = env->GetIntArrayElements(relativeAddress, nullptr);
- for (int i = 0; i < info->relativeAddress.size(); i++) {
- relativeAddressData[i] = info->relativeAddress[i];
+ jint connectionToSinkType;
+ // Relative address maps to HDMI physical address. All addresses are 4 digits long allowing
+ // for a 5–device-deep hierarchy. For more information, refer:
+ // Section 8.7 - Physical Address of HDMI Specification Version 1.3a
+ using android::hardware::display::IDeviceProductInfoConstants;
+ if (info->relativeAddress.size() != 4) {
+ connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_UNKNOWN;
+ } else if (info->relativeAddress[0] == 0) {
+ connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_BUILT_IN;
+ } else if (info->relativeAddress[1] == 0) {
+ connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_DIRECT;
+ } else {
+ connectionToSinkType = IDeviceProductInfoConstants::CONNECTION_TO_SINK_TRANSITIVE;
}
- env->ReleaseIntArrayElements(relativeAddress, relativeAddressData, 0);
return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name,
manufacturerPnpId, productId, modelYear, manufactureDate,
- relativeAddress);
+ connectionToSinkType);
}
static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
@@ -1970,7 +1979,7 @@ int register_android_view_SurfaceControl(JNIEnv* env)
"Ljava/lang/String;"
"Ljava/lang/Integer;"
"Landroid/hardware/display/DeviceProductInfo$ManufactureDate;"
- "[I)V");
+ "I)V");
jclass deviceProductInfoManufactureDateClazz =
FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate");
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
index 10fc18dcd386..980e12d0bb40 100644
--- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android/file_descriptor_jni.h>
+
#include "NetdClient.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -24,9 +26,20 @@ static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject
setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
}
+static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket) {
+ return (jboolean)!protectFromVpn(socket);
+}
+
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static const JNINativeMethod gNetworkUtilMethods[] = {
{"setAllowNetworkingForProcess", "(Z)V",
(void *)android_net_utils_setAllowNetworkingForProcess},
+ {"protectFromVpn", "(I)Z", (void *)android_net_utils_protectFromVpn},
+ {"protectFromVpn", "(Ljava/io/FileDescriptor;)Z",
+ (void *)android_net_utils_protectFromVpnWithFd},
};
int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c9062d8a50bc..836074f1d5f7 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -304,15 +304,14 @@ static std::array<UsapTableEntry, USAP_POOL_SIZE_MAX_LIMIT> gUsapTable;
static FileDescriptorTable* gOpenFdTable = nullptr;
// Must match values in com.android.internal.os.Zygote.
-// Note that there are gaps in the constants:
-// This is to further keep the values consistent with IVold.aidl
+// The values should be consistent with IVold.aidl
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
MOUNT_EXTERNAL_DEFAULT = 1,
- MOUNT_EXTERNAL_INSTALLER = 5,
- MOUNT_EXTERNAL_PASS_THROUGH = 7,
- MOUNT_EXTERNAL_ANDROID_WRITABLE = 8,
- MOUNT_EXTERNAL_COUNT = 9
+ MOUNT_EXTERNAL_INSTALLER = 2,
+ MOUNT_EXTERNAL_PASS_THROUGH = 3,
+ MOUNT_EXTERNAL_ANDROID_WRITABLE = 4,
+ MOUNT_EXTERNAL_COUNT = 5
};
// Must match values in com.android.internal.os.Zygote.
@@ -1401,16 +1400,15 @@ static void insertPackagesToMergedList(JNIEnv* env,
}
static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
- jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
- jstring managed_nice_name, fail_fn_t fail_fn) {
-
- std::vector<std::string> merged_data_info_list;
- insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
- process_name, managed_nice_name, fail_fn);
- insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
- process_name, managed_nice_name, fail_fn);
+ jobjectArray allowlisted_data_info_list, uid_t uid,
+ const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
+ std::vector<std::string> merged_data_info_list;
+ insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list, process_name,
+ managed_nice_name, fail_fn);
+ insertPackagesToMergedList(env, merged_data_info_list, allowlisted_data_info_list, process_name,
+ managed_nice_name, fail_fn);
- isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
/**
@@ -1511,240 +1509,242 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
}
// Utility routine to specialize a zygote child process.
-static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
- jint runtime_flags, jobjectArray rlimits,
- jlong permitted_capabilities, jlong effective_capabilities,
- jint mount_external, jstring managed_se_info,
- jstring managed_nice_name, bool is_system_server,
- bool is_child_zygote, jstring managed_instruction_set,
- jstring managed_app_data_dir, bool is_top_app,
- jobjectArray pkg_data_info_list,
- jobjectArray whitelisted_data_info_list,
- bool mount_data_dirs, bool mount_storage_dirs) {
- const char* process_name = is_system_server ? "system_server" : "zygote";
- auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
- auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
+ jobjectArray rlimits, jlong permitted_capabilities,
+ jlong effective_capabilities, jint mount_external,
+ jstring managed_se_info, jstring managed_nice_name,
+ bool is_system_server, bool is_child_zygote,
+ jstring managed_instruction_set, jstring managed_app_data_dir,
+ bool is_top_app, jobjectArray pkg_data_info_list,
+ jobjectArray allowlisted_data_info_list, bool mount_data_dirs,
+ bool mount_storage_dirs) {
+ const char* process_name = is_system_server ? "system_server" : "zygote";
+ auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+
+ auto se_info = extract_fn(managed_se_info);
+ auto nice_name = extract_fn(managed_nice_name);
+ auto instruction_set = extract_fn(managed_instruction_set);
+ auto app_data_dir = extract_fn(managed_app_data_dir);
+
+ // Keep capabilities across UID change, unless we're staying root.
+ if (uid != 0) {
+ EnableKeepCapabilities(fail_fn);
+ }
- auto se_info = extract_fn(managed_se_info);
- auto nice_name = extract_fn(managed_nice_name);
- auto instruction_set = extract_fn(managed_instruction_set);
- auto app_data_dir = extract_fn(managed_app_data_dir);
+ SetInheritable(permitted_capabilities, fail_fn);
- // Keep capabilities across UID change, unless we're staying root.
- if (uid != 0) {
- EnableKeepCapabilities(fail_fn);
- }
+ DropCapabilitiesBoundingSet(fail_fn);
- SetInheritable(permitted_capabilities, fail_fn);
+ bool need_pre_initialize_native_bridge = !is_system_server && instruction_set.has_value() &&
+ android::NativeBridgeAvailable() &&
+ // Native bridge may be already initialized if this
+ // is an app forked from app-zygote.
+ !android::NativeBridgeInitialized() &&
+ android::NeedsNativeBridge(instruction_set.value().c_str());
- DropCapabilitiesBoundingSet(fail_fn);
+ MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
- bool need_pre_initialize_native_bridge =
- !is_system_server &&
- instruction_set.has_value() &&
- android::NativeBridgeAvailable() &&
- // Native bridge may be already initialized if this
- // is an app forked from app-zygote.
- !android::NativeBridgeInitialized() &&
- android::NeedsNativeBridge(instruction_set.value().c_str());
+ // Make sure app is running in its own mount namespace before isolating its data directories.
+ ensureInAppMountNamespace(fail_fn);
- MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
+ // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
+ // mount all related packages separately.
+ if (mount_data_dirs) {
+ isolateAppData(env, pkg_data_info_list, allowlisted_data_info_list, uid, process_name,
+ managed_nice_name, fail_fn);
+ isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ }
+ // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
+ // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
+ // and hence they won't bind mount storage dirs.
+ if (mount_storage_dirs) {
+ BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name,
+ fail_fn);
+ }
- // Make sure app is running in its own mount namespace before isolating its data directories.
- ensureInAppMountNamespace(fail_fn);
+ // If this zygote isn't root, it won't be able to create a process group,
+ // since the directory is owned by root.
+ if (!is_system_server && getuid() == 0) {
+ const int rc = createProcessGroup(uid, getpid());
+ if (rc == -EROFS) {
+ ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+ } else if (rc != 0) {
+ ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+ }
+ }
- // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind
- // mount all related packages separately.
- if (mount_data_dirs) {
- isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
- uid, process_name, managed_nice_name, fail_fn);
- isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
- }
- // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
- // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
- // and hence they won't bind mount storage dirs.
- if (mount_storage_dirs) {
- BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
- }
+ SetGids(env, gids, is_child_zygote, fail_fn);
+ SetRLimits(env, rlimits, fail_fn);
- // If this zygote isn't root, it won't be able to create a process group,
- // since the directory is owned by root.
- if (!is_system_server && getuid() == 0) {
- const int rc = createProcessGroup(uid, getpid());
- if (rc == -EROFS) {
- ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
- } else if (rc != 0) {
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+ if (need_pre_initialize_native_bridge) {
+ // Due to the logic behind need_pre_initialize_native_bridge we know that
+ // instruction_set contains a value.
+ android::PreInitializeNativeBridge(app_data_dir.has_value() ? app_data_dir.value().c_str()
+ : nullptr,
+ instruction_set.value().c_str());
}
- }
- SetGids(env, gids, is_child_zygote, fail_fn);
- SetRLimits(env, rlimits, fail_fn);
-
- if (need_pre_initialize_native_bridge) {
- // Due to the logic behind need_pre_initialize_native_bridge we know that
- // instruction_set contains a value.
- android::PreInitializeNativeBridge(
- app_data_dir.has_value() ? app_data_dir.value().c_str() : nullptr,
- instruction_set.value().c_str());
- }
+ if (setresgid(gid, gid, gid) == -1) {
+ fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+ }
- if (setresgid(gid, gid, gid) == -1) {
- fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
- }
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case,
+ // before changing uid from 0, which clears capabilities. The other
+ // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
+ // breaks SELinux domain transition (see b/71859146). As the result,
+ // privileged syscalls used below still need to be accessible in app process.
+ SetUpSeccompFilter(uid, is_child_zygote);
- // Must be called when the new process still has CAP_SYS_ADMIN, in this case,
- // before changing uid from 0, which clears capabilities. The other
- // alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
- // breaks SELinux domain transition (see b/71859146). As the result,
- // privileged syscalls used below still need to be accessible in app process.
- SetUpSeccompFilter(uid, is_child_zygote);
+ // Must be called before losing the permission to set scheduler policy.
+ SetSchedulerPolicy(fail_fn, is_top_app);
- // Must be called before losing the permission to set scheduler policy.
- SetSchedulerPolicy(fail_fn, is_top_app);
+ if (setresuid(uid, uid, uid) == -1) {
+ fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+ }
- if (setresuid(uid, uid, uid) == -1) {
- fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
- }
+ // The "dumpable" flag of a process, which controls core dump generation, is
+ // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+ // user or group ID changes. See proc(5) for possible values. In most cases,
+ // the value is 0, so core dumps are disabled for zygote children. However,
+ // when running in a Chrome OS container, the value is already set to 2,
+ // which allows the external crash reporter to collect all core dumps. Since
+ // only system crashes are interested, core dump is disabled for app
+ // processes. This also ensures compliance with CTS.
+ int dumpable = prctl(PR_GET_DUMPABLE);
+ if (dumpable == -1) {
+ ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+ }
- // The "dumpable" flag of a process, which controls core dump generation, is
- // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
- // user or group ID changes. See proc(5) for possible values. In most cases,
- // the value is 0, so core dumps are disabled for zygote children. However,
- // when running in a Chrome OS container, the value is already set to 2,
- // which allows the external crash reporter to collect all core dumps. Since
- // only system crashes are interested, core dump is disabled for app
- // processes. This also ensures compliance with CTS.
- int dumpable = prctl(PR_GET_DUMPABLE);
- if (dumpable == -1) {
- ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
- }
+ if (dumpable == 2 && uid >= AID_APP) {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+ }
+ }
- if (dumpable == 2 && uid >= AID_APP) {
- if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
- ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+ // Set process properties to enable debugging if required.
+ if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
+ EnableDebugger();
+ }
+ if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
+ // simpleperf needs the process to be dumpable to profile it.
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+ }
}
- }
- // Set process properties to enable debugging if required.
- if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
- EnableDebugger();
- }
- if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
- // simpleperf needs the process to be dumpable to profile it.
- if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
- ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+ HeapTaggingLevel heap_tagging_level;
+ switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+ case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+ break;
+ case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
+ break;
+ case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
+ break;
+ default:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+ break;
+ }
+ mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+ // runtime.
+ runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+
+ // Avoid heap zero initialization for applications without MTE. Zero init may
+ // cause app compat problems, use more memory, or reduce performance. While it
+ // would be nice to have them for apps, we will have to wait until they are
+ // proven out, have more efficient hardware, and/or apply them only to new
+ // applications.
+ if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
+ mallopt(M_BIONIC_ZERO_INIT, 0);
}
- }
- HeapTaggingLevel heap_tagging_level;
- switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
- case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
- heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
- break;
- case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
- heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
- break;
- case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
- heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
- break;
- default:
- heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
- break;
- }
- mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+ // runtime.
+ runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
- // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
- runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
+ bool forceEnableGwpAsan = false;
+ switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
+ default:
+ case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+ break;
+ case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
+ forceEnableGwpAsan = true;
+ [[fallthrough]];
+ case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+ }
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+ // runtime.
+ runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
+
+ if (NeedsNoRandomizeWorkaround()) {
+ // Work around ARM kernel ASLR lossage (http://b/5817320).
+ int old_personality = personality(0xffffffff);
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+ }
+ }
- // Avoid heap zero initialization for applications without MTE. Zero init may
- // cause app compat problems, use more memory, or reduce performance. While it
- // would be nice to have them for apps, we will have to wait until they are
- // proven out, have more efficient hardware, and/or apply them only to new
- // applications.
- if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) {
- mallopt(M_BIONIC_ZERO_INIT, 0);
- }
+ SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
+ fail_fn);
- // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
- runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
+ __android_log_close();
+ AStatsSocket_close();
- bool forceEnableGwpAsan = false;
- switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
- default:
- case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
- break;
- case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
- forceEnableGwpAsan = true;
- [[fallthrough]];
- case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
- android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
- }
- // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
- runtime_flags &= ~RuntimeFlags::GWP_ASAN_LEVEL_MASK;
+ const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
+ const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
- if (NeedsNoRandomizeWorkaround()) {
- // Work around ARM kernel ASLR lossage (http://b/5817320).
- int old_personality = personality(0xffffffff);
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+ if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
+ fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_ptr, nice_name_ptr));
}
- }
-
- SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
-
- __android_log_close();
- AStatsSocket_close();
-
- const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
- const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
- if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
- fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed",
- uid, is_system_server, se_info_ptr, nice_name_ptr));
- }
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (nice_name.has_value()) {
+ SetThreadName(nice_name.value());
+ } else if (is_system_server) {
+ SetThreadName("system_server");
+ }
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (nice_name.has_value()) {
- SetThreadName(nice_name.value());
- } else if (is_system_server) {
- SetThreadName("system_server");
- }
+ // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+ UnsetChldSignalHandler();
- // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
- UnsetChldSignalHandler();
+ if (is_system_server) {
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork system server hooks.");
+ }
- if (is_system_server) {
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
- if (env->ExceptionCheck()) {
- fail_fn("Error calling post fork system server hooks.");
+ // TODO(b/117874058): Remove hardcoded label here.
+ static const char* kSystemServerLabel = "u:r:system_server:s0";
+ if (selinux_android_setcon(kSystemServerLabel) != 0) {
+ fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+ }
}
- // TODO(oth): Remove hardcoded label here (b/117874058).
- static const char* kSystemServerLabel = "u:r:system_server:s0";
- if (selinux_android_setcon(kSystemServerLabel) != 0) {
- fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
+ if (is_child_zygote) {
+ initUnsolSocketToSystemServer();
}
- }
- if (is_child_zygote) {
- initUnsolSocketToSystemServer();
- }
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+ is_system_server, is_child_zygote, managed_instruction_set);
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
- is_system_server, is_child_zygote, managed_instruction_set);
+ // Reset the process priority to the default value.
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
- // Reset the process priority to the default value.
- setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
-
- if (env->ExceptionCheck()) {
- fail_fn("Error calling post fork hooks.");
- }
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork hooks.");
+ }
}
static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
@@ -2069,12 +2069,11 @@ static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jcl
NO_PAC_FUNC
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
- JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
- jint runtime_flags, jobjectArray rlimits,
- jint mount_external, jstring se_info, jstring nice_name,
+ JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
+ jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+ jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list,
jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
@@ -2109,14 +2108,11 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
- SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
- capabilities, capabilities,
- mount_external, se_info, nice_name, false,
- is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list,
- whitelisted_data_info_list,
- mount_data_dirs == JNI_TRUE,
- mount_storage_dirs == JNI_TRUE);
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
+ mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
+ instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
+ allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
+ mount_storage_dirs == JNI_TRUE);
}
return pid;
}
@@ -2148,12 +2144,11 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
if (pid == 0) {
// System server prcoess does not need data isolation so no need to
// know pkg_data_info_list.
- SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
- permitted_capabilities, effective_capabilities,
- MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,
+ effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
/* pkg_data_info_list */ nullptr,
- /* whitelisted_data_info_list */ nullptr, false, false);
+ /* allowlisted_data_info_list */ nullptr, false, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2261,7 +2256,7 @@ static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
if (!path_cstr) {
RuntimeAbort(env, __LINE__, "path_cstr == nullptr");
}
- FileDescriptorWhitelist::Get()->Allow(path_cstr);
+ FileDescriptorAllowlist::Get()->Allow(path_cstr);
}
static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
@@ -2296,20 +2291,19 @@ static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
* @param is_top_app If the process is for top (high priority) application
*/
static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
- JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
- jint runtime_flags, jobjectArray rlimits,
- jint mount_external, jstring se_info, jstring nice_name,
- jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
- jboolean mount_data_dirs, jboolean mount_storage_dirs) {
- jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
-
- SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
- capabilities, capabilities,
- mount_external, se_info, nice_name, false,
- is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
- mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
+ JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
+ jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
+ jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
+ jboolean is_top_app, jobjectArray pkg_data_info_list,
+ jobjectArray allowlisted_data_info_list, jboolean mount_data_dirs,
+ jboolean mount_storage_dirs) {
+ jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
+ mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
+ instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
+ allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
+ mount_storage_dirs == JNI_TRUE);
}
/**
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 06a71cb22672..7fa627b3f809 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -31,8 +31,8 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-// Static whitelist of open paths that the zygote is allowed to keep open.
-static const char* kPathWhitelist[] = {
+// Static allowlist of open paths that the zygote is allowed to keep open.
+static const char* kPathAllowlist[] = {
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
@@ -53,118 +53,114 @@ static const char* kPathWhitelist[] = {
static const char kFdPath[] = "/proc/self/fd";
// static
-FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
- if (instance_ == nullptr) {
- instance_ = new FileDescriptorWhitelist();
- }
- return instance_;
+FileDescriptorAllowlist* FileDescriptorAllowlist::Get() {
+ if (instance_ == nullptr) {
+ instance_ = new FileDescriptorAllowlist();
+ }
+ return instance_;
}
static bool IsArtMemfd(const std::string& path) {
return android::base::StartsWith(path, "/memfd:/boot-image-methods.art");
}
-bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
- // Check the static whitelist path.
- for (const auto& whitelist_path : kPathWhitelist) {
- if (path == whitelist_path)
- return true;
- }
+bool FileDescriptorAllowlist::IsAllowed(const std::string& path) const {
+ // Check the static allowlist path.
+ for (const auto& allowlist_path : kPathAllowlist) {
+ if (path == allowlist_path) return true;
+ }
- // Check any paths added to the dynamic whitelist.
- for (const auto& whitelist_path : whitelist_) {
- if (path == whitelist_path)
- return true;
- }
+ // Check any paths added to the dynamic allowlist.
+ for (const auto& allowlist_path : allowlist_) {
+ if (path == allowlist_path) return true;
+ }
- // Framework jars are allowed.
- static const char* kFrameworksPrefix[] = {
- "/system/framework/",
- "/system_ext/framework/",
- };
+ // Framework jars are allowed.
+ static const char* kFrameworksPrefix[] = {
+ "/system/framework/",
+ "/system_ext/framework/",
+ };
- static const char* kJarSuffix = ".jar";
+ static const char* kJarSuffix = ".jar";
- for (const auto& frameworks_prefix : kFrameworksPrefix) {
- if (android::base::StartsWith(path, frameworks_prefix)
- && android::base::EndsWith(path, kJarSuffix)) {
- return true;
+ for (const auto& frameworks_prefix : kFrameworksPrefix) {
+ if (android::base::StartsWith(path, frameworks_prefix) &&
+ android::base::EndsWith(path, kJarSuffix)) {
+ return true;
+ }
}
- }
- // Jars from APEXes are allowed. This matches /apex/**/javalib/*.jar.
- static const char* kApexPrefix = "/apex/";
- static const char* kApexJavalibPathSuffix = "/javalib";
- if (android::base::StartsWith(path, kApexPrefix) && android::base::EndsWith(path, kJarSuffix) &&
- android::base::EndsWith(android::base::Dirname(path), kApexJavalibPathSuffix)) {
- return true;
- }
+ // Jars from APEXes are allowed. This matches /apex/**/javalib/*.jar.
+ static const char* kApexPrefix = "/apex/";
+ static const char* kApexJavalibPathSuffix = "/javalib";
+ if (android::base::StartsWith(path, kApexPrefix) && android::base::EndsWith(path, kJarSuffix) &&
+ android::base::EndsWith(android::base::Dirname(path), kApexJavalibPathSuffix)) {
+ return true;
+ }
- // the in-memory file created by ART through memfd_create is allowed.
- if (IsArtMemfd(path)) {
- return true;
- }
+ // the in-memory file created by ART through memfd_create is allowed.
+ if (IsArtMemfd(path)) {
+ return true;
+ }
- // Whitelist files needed for Runtime Resource Overlay, like these:
- // /system/vendor/overlay/framework-res.apk
- // /system/vendor/overlay-subdir/pg/framework-res.apk
- // /vendor/overlay/framework-res.apk
- // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
- // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
- // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
- // See AssetManager.cpp for more details on overlay-subdir.
- static const char* kOverlayDir = "/system/vendor/overlay/";
- static const char* kVendorOverlayDir = "/vendor/overlay";
- static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
- static const char* kSystemProductOverlayDir = "/system/product/overlay/";
- static const char* kProductOverlayDir = "/product/overlay";
- static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/";
- static const char* kSystemExtOverlayDir = "/system_ext/overlay";
- static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
- static const char* kOdmOverlayDir = "/odm/overlay";
- static const char* kSystemOemOverlayDir = "/system/oem/overlay";
- static const char* kOemOverlayDir = "/oem/overlay";
- static const char* kApkSuffix = ".apk";
-
- if ((android::base::StartsWith(path, kOverlayDir)
- || android::base::StartsWith(path, kVendorOverlaySubdir)
- || android::base::StartsWith(path, kVendorOverlayDir)
- || android::base::StartsWith(path, kSystemProductOverlayDir)
- || android::base::StartsWith(path, kProductOverlayDir)
- || android::base::StartsWith(path, kSystemSystemExtOverlayDir)
- || android::base::StartsWith(path, kSystemExtOverlayDir)
- || android::base::StartsWith(path, kSystemOdmOverlayDir)
- || android::base::StartsWith(path, kOdmOverlayDir)
- || android::base::StartsWith(path, kSystemOemOverlayDir)
- || android::base::StartsWith(path, kOemOverlayDir))
- && android::base::EndsWith(path, kApkSuffix)
- && path.find("/../") == std::string::npos) {
- return true;
- }
+ // Allowlist files needed for Runtime Resource Overlay, like these:
+ // /system/vendor/overlay/framework-res.apk
+ // /system/vendor/overlay-subdir/pg/framework-res.apk
+ // /vendor/overlay/framework-res.apk
+ // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+ // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+ // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+ // See AssetManager.cpp for more details on overlay-subdir.
+ static const char* kOverlayDir = "/system/vendor/overlay/";
+ static const char* kVendorOverlayDir = "/vendor/overlay";
+ static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
+ static const char* kSystemProductOverlayDir = "/system/product/overlay/";
+ static const char* kProductOverlayDir = "/product/overlay";
+ static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/";
+ static const char* kSystemExtOverlayDir = "/system_ext/overlay";
+ static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
+ static const char* kOdmOverlayDir = "/odm/overlay";
+ static const char* kSystemOemOverlayDir = "/system/oem/overlay";
+ static const char* kOemOverlayDir = "/oem/overlay";
+ static const char* kApkSuffix = ".apk";
+
+ if ((android::base::StartsWith(path, kOverlayDir) ||
+ android::base::StartsWith(path, kVendorOverlaySubdir) ||
+ android::base::StartsWith(path, kVendorOverlayDir) ||
+ android::base::StartsWith(path, kSystemProductOverlayDir) ||
+ android::base::StartsWith(path, kProductOverlayDir) ||
+ android::base::StartsWith(path, kSystemSystemExtOverlayDir) ||
+ android::base::StartsWith(path, kSystemExtOverlayDir) ||
+ android::base::StartsWith(path, kSystemOdmOverlayDir) ||
+ android::base::StartsWith(path, kOdmOverlayDir) ||
+ android::base::StartsWith(path, kSystemOemOverlayDir) ||
+ android::base::StartsWith(path, kOemOverlayDir)) &&
+ android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
+ return true;
+ }
- static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
- static const char* kOverlayIdmapSuffix = ".apk@idmap";
- if (android::base::StartsWith(path, kOverlayIdmapPrefix)
- && android::base::EndsWith(path, kOverlayIdmapSuffix)
- && path.find("/../") == std::string::npos) {
- return true;
- }
+ static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
+ static const char* kOverlayIdmapSuffix = ".apk@idmap";
+ if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
+ android::base::EndsWith(path, kOverlayIdmapSuffix) &&
+ path.find("/../") == std::string::npos) {
+ return true;
+ }
- // All regular files that are placed under this path are whitelisted automatically.
- static const char* kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
- if (android::base::StartsWith(path, kZygoteWhitelistPath)
- && path.find("/../") == std::string::npos) {
- return true;
- }
+ // All regular files that are placed under this path are allowlisted
+ // automatically. The directory name is maintained for compatibility.
+ static const char* kZygoteAllowlistPath = "/vendor/zygote_whitelist/";
+ if (android::base::StartsWith(path, kZygoteAllowlistPath) &&
+ path.find("/../") == std::string::npos) {
+ return true;
+ }
- return false;
+ return false;
}
-FileDescriptorWhitelist::FileDescriptorWhitelist()
- : whitelist_() {
-}
+FileDescriptorAllowlist::FileDescriptorAllowlist() : allowlist_() {}
-FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+FileDescriptorAllowlist* FileDescriptorAllowlist::instance_ = nullptr;
// Keeps track of all relevant information (flags, offset etc.) of an
// open zygote file descriptor.
@@ -217,7 +213,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn)
fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
}
- const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+ const FileDescriptorAllowlist* allowlist = FileDescriptorAllowlist::Get();
if (S_ISSOCK(f_stat.st_mode)) {
std::string socket_name;
@@ -225,16 +221,15 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn)
fail_fn("Unable to get socket name");
}
- if (!whitelist->IsAllowed(socket_name)) {
- fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
- socket_name.c_str(),
- fd));
+ if (!allowlist->IsAllowed(socket_name)) {
+ fail_fn(android::base::StringPrintf("Socket name not allowlisted : %s (fd=%d)",
+ socket_name.c_str(), fd));
}
return new FileDescriptorInfo(fd);
}
- // We only handle whitelisted regular files and character devices. Whitelisted
+ // We only handle allowlisted regular files and character devices. Allowlisted
// character devices must provide a guarantee of sensible behaviour when
// reopened.
//
@@ -268,8 +263,8 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn)
strerror(errno)));
}
- if (!whitelist->IsAllowed(file_path)) {
- fail_fn(android::base::StringPrintf("Not whitelisted (%d): %s", fd, file_path.c_str()));
+ if (!allowlist->IsAllowed(file_path)) {
+ fail_fn(android::base::StringPrintf("Not allowlisted (%d): %s", fd, file_path.c_str()));
}
// File descriptor flags : currently on FD_CLOEXEC. We can set these
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 2caf1575981a..14c318e8e84a 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -33,42 +33,40 @@ class FileDescriptorInfo;
// This type is duplicated in com_android_internal_os_Zygote.cpp
typedef const std::function<void(std::string)>& fail_fn_t;
-// Whitelist of open paths that the zygote is allowed to keep open.
+// Allowlist of open paths that the zygote is allowed to keep open.
//
-// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// In addition to the paths listed in kPathAllowlist in file_utils.cpp, and
// paths dynamically added with Allow(), all files ending with ".jar"
-// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// under /system/framework" are allowlisted. See IsAllowed() for the canonical
// definition.
//
-// If the whitelisted path is associated with a regular file or a
+// If the allowlisted path is associated with a regular file or a
// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted path is associated with a
+// offset and mode. If the allowlisted path is associated with a
// AF_UNIX socket, the socket will refer to /dev/null after each
// fork, and all operations on it will fail.
-class FileDescriptorWhitelist {
- public:
- // Lazily creates the global whitelist.
- static FileDescriptorWhitelist* Get();
+class FileDescriptorAllowlist {
+public:
+ // Lazily creates the global allowlist.
+ static FileDescriptorAllowlist* Get();
- // Adds a path to the whitelist.
- void Allow(const std::string& path) {
- whitelist_.push_back(path);
- }
+ // Adds a path to the allowlist.
+ void Allow(const std::string& path) { allowlist_.push_back(path); }
- // Returns true iff. a given path is whitelisted. A path is whitelisted
- // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
- // under /system/framework that ends with ".jar" or if it is a system
- // framework overlay.
- bool IsAllowed(const std::string& path) const;
+ // Returns true iff. a given path is allowlisted. A path is allowlisted
+ // if it belongs to the allowlist (see kPathAllowlist) or if it's a path
+ // under /system/framework that ends with ".jar" or if it is a system
+ // framework overlay.
+ bool IsAllowed(const std::string& path) const;
- private:
- FileDescriptorWhitelist();
+private:
+ FileDescriptorAllowlist();
- static FileDescriptorWhitelist* instance_;
+ static FileDescriptorAllowlist* instance_;
- std::vector<std::string> whitelist_;
+ std::vector<std::string> allowlist_;
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorAllowlist);
};
// A FileDescriptorTable is a collection of FileDescriptorInfo objects
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 99fd21592411..e62b5c102a59 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -15,6 +15,7 @@ per-file settings_enums.proto=tmfang@google.com
ogunwale@google.com
jjaggi@google.com
roosa@google.com
+per-file package_item_info.proto = toddke@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ec41a47a8798..74a37cadc54c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -628,6 +628,7 @@ message ActivityManagerServiceDumpProcessesProto {
optional string tag = 3;
optional int32 type = 4;
optional int32 reason_code = 5;
+ optional int32 calling_uid = 6;
}
repeated PendingTempWhitelist pending_temp_whitelist = 26;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index ec502c3c272f..f26bf7cdb6c1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -337,7 +337,7 @@ message ActivityRecordProto {
optional bool starting_displayed = 20;
optional bool starting_moved = 201;
optional bool visible_set_from_transferred_starting_window = 22;
- repeated .android.graphics.RectProto frozen_bounds = 23;
+ repeated .android.graphics.RectProto frozen_bounds = 23 [deprecated=true];
optional bool visible = 24;
reserved 25; // configuration_container
optional IdentifierProto identifier = 26 [deprecated=true];
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d5f5d28aa7a2..072bb8799e59 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1876,15 +1876,20 @@
<permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi @hide Allows system APK to update Wifi/Cellular coex channels to avoid.
+ <!-- @SystemApi @hide Allows applications to update Wifi/Cellular coex channels to avoid.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|role" />
+
+ <!-- @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.
@@ -2371,6 +2376,15 @@
<permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a {@link android.telecom.CallDiagnosticService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_CALL_DIAGNOSTIC_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link android.telecom.CallRedirectionService},
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
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.xml b/core/res/res/values/attrs.xml
index 100983bcef0d..efc8fe9aafa2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8424,6 +8424,9 @@
<!-- Fully qualified class name of an activity that allows the user to modify
the settings for this service. -->
<attr name="settingsActivity" />
+ <!-- Fully qualified class name of an activity that allows the user to view any passwords
+ saved by this service. -->
+ <attr name="passwordsActivity" format="string" />
<!-- Specifies whether the AutofillService supports inline suggestions-->
<attr name="supportsInlineSuggestions" format="boolean" />
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 12cb3980f785..f38264c41211 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -848,6 +848,15 @@
<!-- y-intercept --> <item>1.000000000000000</item>
</string-array>
+ <!-- Default strength, in percentage, of bright color reduction when activated. -->
+ <integer name="config_reduceBrightColorsStrengthDefault">0</integer>
+
+ <!-- Minimum strength, in percentage, supported by bright color reduction. -->
+ <integer name="config_reduceBrightColorsStrengthMin">0</integer>
+
+ <!-- Maximum strength, in percentage, supported by bright color reduction. -->
+ <integer name="config_reduceBrightColorsStrengthMax">100</integer>
+
<!-- Boolean indicating whether display white balance is supported. -->
<bool name="config_displayWhiteBalanceAvailable">false</bool>
@@ -1783,7 +1792,7 @@
* SDK level 28 makes the following algorithms mandatory : "cbc(aes)", "hmac(md5)",
"hmac(sha1)", "hmac(sha256)", "hmac(sha384)", "hmac(sha512)", "rfc4106(gcm(aes))"
* SDK level 31 makes the following algorithms mandatory : "rfc3686(ctr(aes))",
- "xcbc(aes)", "rfc7539esp(chacha20,poly1305)"
+ "xcbc(aes)", "cmac(aes)", "rfc7539esp(chacha20,poly1305)"
-->
<string-array name="config_optionalIpSecAlgorithms" translatable="false">
<!-- Add algorithm here -->
@@ -1955,6 +1964,8 @@
<string name="config_systemContacts" translatable="false">com.android.contacts</string>
<!-- The name of the package that will hold the speech recognizer role by default. -->
<string name="config_systemSpeechRecognizer" translatable="false"></string>
+ <!-- The name of the package that will hold the system Wi-Fi coex manager role. -->
+ <string name="config_systemWifiCoexManager" translateable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -3939,6 +3950,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>
@@ -4653,20 +4668,11 @@
<!-- 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
- maximum screen area that can be occupied by the app in the letterbox mode. -->
- <item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item>
+ <!-- Aspect ratio of letterboxing for fixed orientation. Values <= 1.0 will be ignored.
+ Note: Activity min/max aspect ratio restrictions will still be respected.
+ Therefore this override can control the maximum screen area that can be occupied by
+ the app in the letterbox mode. -->
+ <item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
<!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
corners of the activity won't be rounded. -->
@@ -4692,6 +4698,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/public.xml b/core/res/res/values/public.xml
index 2004d0a8d15c..7702ee437c43 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3085,6 +3085,7 @@
<public name="hand_secondTint"/>
<public name="hand_secondTintMode"/>
<public name="dataExtractionRules"/>
+ <public name="passwordsActivity"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
@@ -3168,6 +3169,8 @@
<public name="config_customMediaSessionPolicyProvider" />
<!-- @hide @SystemApi -->
<public name="config_systemSpeechRecognizer" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemWifiCoexManager" />
</public-group>
<public-group type="id" first-id="0x01020055">
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 dbb584dfe293..2fff7b5f6298 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" />
@@ -3224,6 +3242,9 @@
<java-symbol type="bool" name="config_reduceBrightColorsAvailable" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
<java-symbol type="array" name="config_reduceBrightColorsCoefficientsNonlinear" />
+ <java-symbol type="integer" name="config_reduceBrightColorsStrengthDefault" />
+ <java-symbol type="integer" name="config_reduceBrightColorsStrengthMin" />
+ <java-symbol type="integer" name="config_reduceBrightColorsStrengthMax" />
<java-symbol type="array" name="config_availableColorModes" />
<java-symbol type="array" name="config_mappedColorModes" />
<java-symbol type="string" name="config_vendorColorModesRestoreHint" />
@@ -3293,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" />
@@ -4029,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" />
@@ -4050,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" />
@@ -4157,12 +4177,11 @@
<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" />
- <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
+ <java-symbol type="dimen" name="config_fixedOrientationLetterboxAspectRatio" />
<java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
<java-symbol type="integer" name="config_letterboxBackgroundType" />
<java-symbol type="color" name="config_letterboxBackgroundColor" />
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/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 0fe44630f308..9e8837386128 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Test;
import java.util.List;
@@ -67,9 +69,9 @@ public class SearchSpecTest {
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addProjection("TypeA", "field1", "field2.subfield2")
- .addProjection("TypeB", "field7")
- .addProjection("TypeC")
+ .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
+ .addProjection("TypeB", ImmutableList.of("field7"))
+ .addProjection("TypeC", ImmutableList.of())
.build();
Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
index bf6b07f71839..677f4bd9e46d 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
@@ -42,13 +42,13 @@ public class SetSchemaRequestTest {
}
@Test
- public void testInvalidSchemaReferences_fromSystemUiVisibility() {
+ public void testInvalidSchemaReferences_fromDisplayedBySystem() {
IllegalArgumentException expected =
expectThrows(
IllegalArgumentException.class,
() ->
new SetSchemaRequest.Builder()
- .setSchemaTypeVisibilityForSystemUi("InvalidSchema", false)
+ .setSchemaTypeDisplayedBySystem("InvalidSchema", false)
.build());
assertThat(expected).hasMessageThat().contains("referenced, but were not added");
}
@@ -71,30 +71,30 @@ public class SetSchemaRequestTest {
}
@Test
- public void testSchemaTypeVisibilityForSystemUi_visible() {
+ public void testSetSchemaTypeDisplayedBySystem_displayed() {
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
- // By default, the schema is visible.
+ // By default, the schema is displayed.
SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
- assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
+ assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
request =
new SetSchemaRequest.Builder()
.addSchemas(schema)
- .setSchemaTypeVisibilityForSystemUi("Schema", true)
+ .setSchemaTypeDisplayedBySystem("Schema", true)
.build();
- assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
+ assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
}
@Test
- public void testSchemaTypeVisibilityForSystemUi_notVisible() {
+ public void testSetSchemaTypeDisplayedBySystem_notDisplayed() {
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
SetSchemaRequest request =
new SetSchemaRequest.Builder()
.addSchemas(schema)
- .setSchemaTypeVisibilityForSystemUi("Schema", false)
+ .setSchemaTypeDisplayedBySystem("Schema", false)
.build();
- assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
+ assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema");
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 50b52eb2a0ea..6c8b94129c82 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -148,13 +148,15 @@ public class ObjectPoolTests {
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
IBinder assistToken = new Binder();
+ IBinder shareableActivityToken = new Binder();
Supplier<LaunchActivityItem> itemSupplier = () -> new LaunchActivityItemBuilder()
.setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
.setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer)
.setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
.setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
- .setIsForward(true).setAssistToken(assistToken).build();
+ .setIsForward(true).setAssistToken(assistToken)
+ .setShareableActivityToken(shareableActivityToken).build();
LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
LaunchActivityItem item = itemSupplier.get();
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 02e75dd76a07..1a06789e729f 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -109,6 +109,7 @@ class TestUtils {
private boolean mIsForward;
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
+ private IBinder mShareableActivityToken;
private FixedRotationAdjustments mFixedRotationAdjustments;
LaunchActivityItemBuilder setIntent(Intent intent) {
@@ -196,6 +197,11 @@ class TestUtils {
return this;
}
+ LaunchActivityItemBuilder setShareableActivityToken(IBinder shareableActivityToken) {
+ mShareableActivityToken = shareableActivityToken;
+ return this;
+ }
+
LaunchActivityItemBuilder setFixedRotationAdjustments(FixedRotationAdjustments fra) {
mFixedRotationAdjustments = fra;
return this;
@@ -206,7 +212,8 @@ class TestUtils {
mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
- null /* activityClientController */, mFixedRotationAdjustments);
+ null /* activityClientController */, mFixedRotationAdjustments,
+ mShareableActivityToken);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f6d985b1788f..6f3d7ae5eb3c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -207,6 +207,7 @@ public class TransactionParcelTests {
.setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic())
.setPendingNewIntents(referrerIntentList()).setIsForward(true)
.setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
+ .setShareableActivityToken(new Binder())
.build();
writeAndPrepareForReading(item);
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/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
new file mode 100644
index 000000000000..67c1b3c947fb
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BytesMatcherTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.os;
+
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+
+import android.bluetooth.BluetoothUuid;
+import android.net.MacAddress;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class BytesMatcherTest extends TestCase {
+ @Test
+ public void testEmpty() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("");
+ assertFalse(matcher.test(hexStringToByteArray("cafe")));
+ assertFalse(matcher.test(hexStringToByteArray("")));
+ }
+
+ @Test
+ public void testExact() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+cafe");
+ assertTrue(matcher.test(hexStringToByteArray("cafe")));
+ assertFalse(matcher.test(hexStringToByteArray("beef")));
+ assertFalse(matcher.test(hexStringToByteArray("ca")));
+ assertFalse(matcher.test(hexStringToByteArray("cafe00")));
+ }
+
+ @Test
+ public void testMask() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
+ assertTrue(matcher.test(hexStringToByteArray("cafe")));
+ assertTrue(matcher.test(hexStringToByteArray("ca88")));
+ assertFalse(matcher.test(hexStringToByteArray("beef")));
+ assertFalse(matcher.test(hexStringToByteArray("ca")));
+ assertFalse(matcher.test(hexStringToByteArray("cafe00")));
+ }
+
+ @Test
+ public void testMacAddress() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
+ assertTrue(matcher.testMacAddress(
+ MacAddress.fromString("ca:fe:00:00:00:00")));
+ assertFalse(matcher.testMacAddress(
+ MacAddress.fromString("f0:0d:00:00:00:00")));
+ }
+
+ @Test
+ public void testBluetoothUuid() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
+ assertTrue(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe"))));
+ assertFalse(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef"))));
+ }
+
+ /**
+ * Verify that single matcher can be configured to match Bluetooth UUIDs of
+ * varying lengths.
+ */
+ @Test
+ public void testBluetoothUuid_Mixed() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000");
+ assertTrue(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa"))));
+ assertFalse(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb"))));
+ assertTrue(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb"))));
+ assertFalse(matcher.testBluetoothUuid(
+ BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa"))));
+ }
+
+ @Test
+ public void testSerialize() throws Exception {
+ BytesMatcher matcher = new BytesMatcher();
+ matcher.addRejectRule(hexStringToByteArray("cafe00112233"),
+ hexStringToByteArray("ffffff000000"));
+ matcher.addRejectRule(hexStringToByteArray("beef00112233"),
+ null);
+ matcher.addAcceptRule(hexStringToByteArray("000000000000"),
+ hexStringToByteArray("000000000000"));
+
+ assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
+ assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
+ assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
+
+ // Bounce through serialization pass and confirm it still works
+ matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
+
+ assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
+ assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
+ assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
+ }
+
+ @Test
+ public void testOrdering_RejectFirst() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
+ assertFalse(matcher.test(hexStringToByteArray("ff")));
+ assertTrue(matcher.test(hexStringToByteArray("f0")));
+ assertFalse(matcher.test(hexStringToByteArray("0f")));
+ }
+
+ @Test
+ public void testOrdering_AcceptFirst() throws Exception {
+ BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f");
+ assertTrue(matcher.test(hexStringToByteArray("ff")));
+ assertTrue(matcher.test(hexStringToByteArray("f0")));
+ assertFalse(matcher.test(hexStringToByteArray("0f")));
+ }
+}
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/core/tests/coretests/src/android/security/CredentialManagementAppTest.java b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
index 366aabd183e3..fa824b140caa 100644
--- a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
+++ b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java
@@ -37,8 +37,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.util.Iterator;
-import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -149,37 +147,6 @@ public final class CredentialManagementAppTest {
private void assertCredentialManagementAppsEqual(CredentialManagementApp actual,
CredentialManagementApp expected) {
assertThat(actual.getPackageName(), is(expected.getPackageName()));
- assertAuthenticationPoliciesEqual(actual.getAuthenticationPolicy(),
- expected.getAuthenticationPolicy());
- }
-
- private void assertAuthenticationPoliciesEqual(AppUriAuthenticationPolicy actual,
- AppUriAuthenticationPolicy expected) {
- Iterator<Map.Entry<String, Map<Uri, String>>> actualIter =
- actual.getAppAndUriMappings().entrySet().iterator();
- Iterator<Map.Entry<String, Map<Uri, String>>> expectedIter =
- expected.getAppAndUriMappings().entrySet().iterator();
-
- assertThat(actual.getAppAndUriMappings().size(),
- is(expected.getAppAndUriMappings().size()));
- while (actualIter.hasNext()) {
- Map.Entry<String, Map<Uri, String>> actualAppToUri = actualIter.next();
- Map.Entry<String, Map<Uri, String>> expectedAppToUri = expectedIter.next();
- assertThat(actualAppToUri.getKey(), is(expectedAppToUri.getKey()));
- assertUrisToAliasesEqual(actualAppToUri.getValue(), expectedAppToUri.getValue());
- }
- }
-
- private void assertUrisToAliasesEqual(Map<Uri, String> actual, Map<Uri, String> expected) {
- Iterator<Map.Entry<Uri, String>> actualIter = actual.entrySet().iterator();
- Iterator<Map.Entry<Uri, String>> expectedIter = expected.entrySet().iterator();
-
- assertThat(actual.size(), is(expected.size()));
- while (actualIter.hasNext()) {
- Map.Entry<Uri, String> actualUriToAlias = actualIter.next();
- Map.Entry<Uri, String> expectedUriToAlias = expectedIter.next();
- assertThat(actualUriToAlias.getKey(), is(expectedUriToAlias.getKey()));
- assertThat(actualUriToAlias.getValue(), is(expectedUriToAlias.getValue()));
- }
+ assertThat(actual.getAuthenticationPolicy(), is(expected.getAuthenticationPolicy()));
}
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index ab24f89015c7..7e1e7f4bdd7f 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -33,9 +33,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import java.util.Arrays;
-import java.util.List;
-
/**
* Tests for AccessibilityInteractionClient
*/
@@ -65,7 +62,7 @@ public class AccessibilityInteractionClientTest {
final long accessibilityNodeId = 0x4321L;
AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain();
nodeFromConnection.setSourceNodeId(accessibilityNodeId, windowId);
- mMockConnection.mInfosToReturn = Arrays.asList(nodeFromConnection);
+ mMockConnection.mInfoToReturn = nodeFromConnection;
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
AccessibilityNodeInfo node = client.findAccessibilityNodeInfoByAccessibilityId(
MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
@@ -75,7 +72,7 @@ public class AccessibilityInteractionClientTest {
}
private static class MockConnection extends AccessibilityServiceConnectionImpl {
- List<AccessibilityNodeInfo> mInfosToReturn;
+ AccessibilityNodeInfo mInfoToReturn;
@Override
public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
@@ -83,7 +80,7 @@ public class AccessibilityInteractionClientTest {
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
Bundle arguments) {
try {
- callback.setFindAccessibilityNodeInfosResult(mInfosToReturn, interactionId);
+ callback.setFindAccessibilityNodeInfoResult(mInfoToReturn, interactionId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/core/tests/coretests/src/android/view/accessibility/OWNERS b/core/tests/coretests/src/android/view/accessibility/OWNERS
new file mode 100644
index 000000000000..b74281edbf52
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
index 4680a640a7ff..ddb6729b55e1 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
@@ -17,14 +17,17 @@ package android.view.contentcapture;
import static com.google.common.truth.Truth.assertThat;
+import android.app.assist.ActivityId;
import android.content.ComponentName;
+import android.os.Binder;
+import android.os.IBinder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
- * Unit test for {@link ContentCaptureEvent}.
+ * Unit test for {@link ContentCaptureContext}.
*
* <p>To run it:
* {@code atest FrameworksCoreTests:android.view.contentcapture.ContentCaptureContextTest}
@@ -35,13 +38,17 @@ public class ContentCaptureContextTest {
@Test
public void testConstructorAdditionalFlags() {
final ComponentName componentName = new ComponentName("component", "name");
+ final IBinder token = new Binder();
final ContentCaptureContext ctx = new ContentCaptureContext(/* clientContext= */ null,
- componentName, /* taskId= */ 666, /* displayId= */ 42, /* flags= */ 1);
+ new ActivityId(/* taskId= */ 666, token), componentName, /* displayId= */
+ 42, /* flags= */ 1);
final ContentCaptureContext newCtx = new ContentCaptureContext(ctx, /* extraFlags= */ 2);
assertThat(newCtx.getFlags()).isEqualTo(3);
-
assertThat(newCtx.getActivityComponent()).isEqualTo(componentName);
- assertThat(newCtx.getTaskId()).isEqualTo(666);
+ ActivityId activityId = newCtx.getActivityId();
+ assertThat(activityId).isNotNull();
+ assertThat(activityId.getTaskId()).isEqualTo(666);
+ assertThat(activityId.getToken()).isEqualTo(token);
assertThat(newCtx.getDisplayId()).isEqualTo(42);
assertThat(newCtx.getExtras()).isNull();
assertThat(newCtx.getLocusId()).isNull();
diff --git a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
index bec3d1644e81..cf126c62dac7 100644
--- a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class DischargedPowerCalculatorTest {
+public class BatteryChargeCalculatorTest {
private static final double PRECISION = 0.00001;
@Rule
@@ -40,27 +40,41 @@ public class DischargedPowerCalculatorTest {
@Test
public void testDischargeTotals() {
+ BatteryChargeCalculator calculator =
+ new BatteryChargeCalculator(mStatsRule.getPowerProfile());
+
final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
mStatsRule.setTime(1000, 1000);
batteryStats.resetAllStatsCmdLocked();
batteryStats.setNoAutoReset(true);
batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
- /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
- 1_000_000, 1_000_000);
+ /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ 1_000_000, 1_000_000, 1_000_000);
batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
- /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
- 2_000_000, 2_000_000);
-
- DischargedPowerCalculator calculator =
- new DischargedPowerCalculator(mStatsRule.getPowerProfile());
+ /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
+ 1_500_000, 1_500_000, 1_500_000);
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
+ 2_000_000, 2_000_000, 2_000_000);
- final BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
+ BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
.isWithin(PRECISION).of(360.0);
assertThat(batteryUsageStats.getDischargedPowerRange().getUpper())
.isWithin(PRECISION).of(400.0);
+ assertThat(batteryUsageStats.getBatteryTimeRemainingMs()).isEqualTo(8_000_000);
+ assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(-1);
+
+ // Plug in
+ batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_CHARGING, 100,
+ BatteryManager.BATTERY_PLUGGED_USB, 80, 72, 3700, 2_400_000, 4_000_000, 100,
+ 4_000_000, 4_000_000, 4_000_000);
+
+ batteryUsageStats = mStatsRule.apply(calculator);
+
+ assertThat(batteryUsageStats.getChargeTimeRemainingMs()).isEqualTo(100_000);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 74c37ada2054..ee472880b79f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -23,6 +23,7 @@ import org.junit.runners.Suite;
@Suite.SuiteClasses({
AmbientDisplayPowerCalculatorTest.class,
AudioPowerCalculatorTest.class,
+ BatteryChargeCalculatorTest.class,
BatteryStatsCpuTimesTest.class,
BatteryStatsBackgroundStatsTest.class,
BatteryStatsBinderCallStatsTest.class,
@@ -49,7 +50,6 @@ import org.junit.runners.Suite;
CameraPowerCalculatorTest.class,
CpuPowerCalculatorTest.class,
CustomMeasuredPowerCalculatorTest.class,
- DischargedPowerCalculatorTest.class,
FlashlightPowerCalculatorTest.class,
GnssPowerCalculatorTest.class,
IdlePowerCalculatorTest.class,
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index d2b20b4298d9..0808186b21c5 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -298,7 +298,7 @@ public class ActivityThreadClientTest {
null /* pendingResults */, null /* pendingNewIntents */,
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
mThread /* client */, null /* asssitToken */,
- null /* fixedRotationAdjustments */);
+ null /* fixedRotationAdjustments */, null /* shareableActivityToken */);
}
@Override
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 2f41b9084f40..89644e2320c1 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -146,6 +146,13 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "allowed_privapp_com.android.car.rotary",
+ sub_dir: "permissions",
+ src: "com.android.car.rotary.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "allowed_privapp_com.android.car.ui.paintbooth",
sub_dir: "permissions",
src: "com.android.car.ui.paintbooth.xml",
diff --git a/data/etc/car/android.car.cluster.xml b/data/etc/car/android.car.cluster.xml
index d7f29da7a356..de3accafa2fa 100644
--- a/data/etc/car/android.car.cluster.xml
+++ b/data/etc/car/android.car.cluster.xml
@@ -20,5 +20,7 @@
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.car.permission.CAR_ENGINE_DETAILED"/>
+ <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml
index c3642d886180..2ff98357f8bf 100644
--- a/data/etc/car/com.android.car.bugreport.xml
+++ b/data/etc/car/com.android.car.bugreport.xml
@@ -21,5 +21,6 @@
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
+ <permission name="android.car.permission.CAR_DRIVING_STATE"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index 0e49284d106f..ac16af348471 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -17,9 +17,10 @@
<permissions>
<privapp-permissions package="com.android.car.carlauncher">
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml
index d44f5a1704a2..61ae53a30209 100644
--- a/data/etc/car/com.android.car.dialer.xml
+++ b/data/etc/car/com.android.car.dialer.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.car.dialer">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml
index d3631e067f8b..534d44da9a7c 100644
--- a/data/etc/car/com.android.car.hvac.xml
+++ b/data/etc/car/com.android.car.hvac.xml
@@ -17,5 +17,6 @@
<permissions>
<privapp-permissions package="com.android.car.hvac">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml
index d7853aba2d90..ed8652c01647 100644
--- a/data/etc/car/com.android.car.radio.xml
+++ b/data/etc/car/com.android.car.radio.xml
@@ -18,5 +18,7 @@
<privapp-permissions package="com.android.car.radio">
<permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml
new file mode 100644
index 000000000000..eddef1acbbc7
--- /dev/null
+++ b/data/etc/car/com.android.car.rotary.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.
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.rotary">
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
+
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index bd30d7a61517..e6196c296552 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -51,5 +51,41 @@
<!-- use for rotary fragment to enable/disable packages related to rotary -->
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+
+ <!-- CarService permissions -->
+ <!-- TODO: Explain why so many permissions are required -->
+ <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
+ <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
+ <permission name="android.car.permission.CAR_DIAGNOSTICS"/>
+ <permission name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"/>
+ <permission name="android.car.permission.CAR_DRIVING_STATE"/>
+ <permission name="android.car.permission.CAR_DYNAMICS_STATE"/>
+ <permission name="android.car.permission.CAR_EXTERIOR_LIGHTS"/>
+ <permission name="android.car.permission.CAR_IDENTIFICATION"/>
+ <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
+ <permission name="android.car.permission.CAR_MILEAGE"/>
+ <permission name="android.car.permission.CAR_MOCK_VEHICLE_HAL"/>
+ <permission name="android.car.permission.CAR_NAVIGATION_MANAGER"/>
+ <permission name="android.car.permission.CAR_POWER"/>
+ <permission name="android.car.permission.CAR_PROJECTION"/>
+ <permission name="android.car.permission.CAR_TIRES"/>
+ <permission name="android.car.permission.CAR_TEST_SERVICE"/>
+ <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"/>
+ <permission name="android.car.permission.CAR_VENDOR_EXTENSION"/>
+ <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
+ <permission name="android.car.permission.CONTROL_CAR_DOORS"/>
+ <permission name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"/>
+ <permission name="android.car.permission.CONTROL_CAR_FEATURES"/>
+ <permission name="android.car.permission.CONTROL_CAR_MIRRORS"/>
+ <permission name="android.car.permission.CONTROL_CAR_SEATS"/>
+ <permission name="android.car.permission.CONTROL_CAR_WINDOWS"/>
+ <permission name="android.car.permission.GET_CAR_VENDOR_CATEGORY_INFO"/>
+ <permission name="android.car.permission.GET_CAR_VENDOR_CATEGORY_SEAT"/>
+ <permission name="android.car.permission.READ_CAR_STEERING"/>
+ <permission name="android.car.permission.SET_CAR_VENDOR_CATEGORY_INFO"/>
+ <permission name="android.car.permission.STORAGE_MONITORING"/>
+ <permission name="android.car.permission.VMS_PUBLISHER"/>
+ <permission name="android.car.permission.VMS_SUBSCRIBER"/>
</privapp-permissions>
</permissions>
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/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3900d7e674ca..fae89d65ddd1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -68,6 +68,11 @@ applications that come with the platform
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.imsserviceentitlement">
+ <permission name="android.permission.MODIFY_PHONE_STATE" />
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ </privapp-permissions>
+
<privapp-permissions package="com.android.launcher3">
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
@@ -251,6 +256,7 @@ applications that come with the platform
<!-- Permissions required for reading DeviceConfig -->
<permission name="android.permission.READ_DEVICE_CONFIG" />
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+ <permission name="android.permission.MODIFY_QUIET_MODE"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.telephony">
@@ -478,6 +484,8 @@ applications that come with the platform
<permission name="android.permission.SIGNAL_REBOOT_READINESS" />
<!-- Permission required for CTS test - PeopleManagerTest -->
<permission name="android.permission.READ_PEOPLE_DATA" />
+ <!-- Permission required for CTS test - UiTranslationManagerTest -->
+ <permission name="android.permission.MANAGE_UI_TRANSLATION" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
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/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 4f188cc03282..66b8eaec4bcf 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -245,7 +245,7 @@
<alias name="monaco" to="monospace" />
<family name="serif-monospace">
- <font weight="400" style="normal">CutiveMono.ttf</font>
+ <font weight="400" style="normal">CutiveMono-Regular.ttf</font>
</family>
<alias name="courier" to="serif-monospace" />
<alias name="courier new" to="serif-monospace" />
@@ -255,7 +255,7 @@
</family>
<family name="cursive">
- <font weight="400" style="normal">DancingScript-Regular.ttf</font>
+ <font weight="400" style="normal">DancingScript.ttf</font>
<font weight="700" style="normal">DancingScript-Bold.ttf</font>
</family>
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index d1e94dfed4a8..a927f53e3b5f 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -36,11 +36,12 @@ java_library_host {
java_test_host {
name: "error_prone_android_framework_test",
- test_suites: ["general-tests"],
srcs: ["tests/java/**/*.java"],
java_resource_dirs: ["tests/res"],
java_resources: [":error_prone_android_framework_testdata"],
static_libs: [
+ "truth-prebuilt",
+ "kxml2-2.3.0",
"error_prone_android_framework_lib",
"error_prone_test_helpers",
"hamcrest-library",
@@ -48,6 +49,9 @@ java_test_host {
"platform-test-annotations",
"junit",
],
+ test_options: {
+ unit_test: true,
+ },
}
filegroup {
diff --git a/errorprone/TEST_MAPPING b/errorprone/TEST_MAPPING
deleted file mode 100644
index ee4552fb3b33..000000000000
--- a/errorprone/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "error_prone_android_framework_test"
- }
- ]
-}
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/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index f6f770be3de8..da5162b9e15e 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -719,11 +719,11 @@ public final class RenderNode {
/** @hide */
public boolean stretch(float left, float top, float right, float bottom,
float vecX, float vecY, float maxStretchAmount) {
- if (1.0 < vecX || vecX < -1.0) {
- throw new IllegalArgumentException("vecX must be in the range [-1, 1], was " + vecX);
+ if (Float.isInfinite(vecX) || Float.isNaN(vecX)) {
+ throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX);
}
- if (1.0 < vecY || vecY < -1.0) {
- throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
+ if (Float.isInfinite(vecY) || Float.isNaN(vecY)) {
+ throw new IllegalArgumentException("vecY must be a finite, non-NaN value " + vecY);
}
if (top >= bottom || left >= right) {
throw new IllegalArgumentException(
@@ -734,7 +734,16 @@ public final class RenderNode {
throw new IllegalArgumentException(
"The max stretch amount must be >0, got " + maxStretchAmount);
}
- return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
+ return nStretch(
+ mNativeRenderNode,
+ left,
+ top,
+ right,
+ bottom,
+ vecX,
+ vecY,
+ maxStretchAmount
+ );
}
/**
diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java
index 0244ce97c0d4..df79912128fe 100644
--- a/keystore/java/android/security/AppUriAuthenticationPolicy.java
+++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java
@@ -238,4 +238,21 @@ public final class AppUriAuthenticationPolicy implements Parcelable {
return aliases;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AppUriAuthenticationPolicy)) {
+ return false;
+ }
+ AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj;
+ return Objects.equals(mAppToUris, other.mAppToUris);
+ }
+
+ @Override
+ public int hashCode() {
+ return mAppToUris.hashCode();
+ }
+
}
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/keystore/java/android/security/UrisToAliases.java b/keystore/java/android/security/UrisToAliases.java
index 65d433abe166..9a8b659f3db4 100644
--- a/keystore/java/android/security/UrisToAliases.java
+++ b/keystore/java/android/security/UrisToAliases.java
@@ -30,6 +30,7 @@ import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* The mapping from URI to alias, which determines the alias to use when the user visits a URI.
@@ -135,4 +136,21 @@ public final class UrisToAliases implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mUrisToAliases);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof UrisToAliases)) {
+ return false;
+ }
+ UrisToAliases other = (UrisToAliases) obj;
+ return Objects.equals(mUrisToAliases, other.mUrisToAliases);
+ }
+
+ @Override
+ public int hashCode() {
+ return mUrisToAliases.hashCode();
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c79c12cd3343..72735a787b7f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -279,8 +279,10 @@ import javax.security.auth.x500.X500Principal;
* }
*/
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
- private static final X500Principal DEFAULT_CERT_SUBJECT =
+ private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
new X500Principal("CN=Android Keystore Key");
+ private static final X500Principal DEFAULT_SELF_SIGNED_CERT_SUBJECT =
+ new X500Principal("CN=Fake");
private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
@@ -366,7 +368,11 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
if (certificateSubject == null) {
- certificateSubject = DEFAULT_CERT_SUBJECT;
+ if (attestationChallenge == null) {
+ certificateSubject = DEFAULT_SELF_SIGNED_CERT_SUBJECT;
+ } else {
+ certificateSubject = DEFAULT_ATTESTATION_CERT_SUBJECT;
+ }
}
if (certificateNotBefore == null) {
certificateNotBefore = DEFAULT_CERT_NOT_BEFORE;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 7376d9898ab8..85bd24c1c2bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -24,7 +24,9 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -41,9 +43,11 @@ public class ShellInitImpl {
private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<AppPairsController> mAppPairsOptional;
+ private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
+ private final Optional<StartingSurface> mStartingSurfaceOptional;
private final InitImpl mImpl = new InitImpl();
@@ -53,6 +57,8 @@ public class ShellInitImpl {
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
+ Optional<StartingSurface> startingSurfaceOptional,
+ Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -62,6 +68,8 @@ public class ShellInitImpl {
legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
+ startingSurfaceOptional,
+ pipTouchHandlerOptional,
fullscreenTaskListener,
transitions,
mainExecutor).mImpl;
@@ -73,6 +81,8 @@ public class ShellInitImpl {
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
+ Optional<StartingSurface> startingSurfaceOptional,
+ Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
@@ -83,8 +93,10 @@ public class ShellInitImpl {
mSplitScreenOptional = splitScreenOptional;
mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
+ mPipTouchHandlerOptional = pipTouchHandlerOptional;
mTransitions = transitions;
mMainExecutor = mainExecutor;
+ mStartingSurfaceOptional = startingSurfaceOptional;
}
private void init() {
@@ -93,6 +105,7 @@ public class ShellInitImpl {
mShellTaskOrganizer.addListenerForType(
mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface);
// Register the shell organizer
mShellTaskOrganizer.registerOrganizer();
@@ -105,6 +118,11 @@ public class ShellInitImpl {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitions.register(mShellTaskOrganizer);
}
+
+ // TODO(b/181599115): This should really be the pip controller, but until we can provide the
+ // controller instead of the feature interface, can just initialize the touch handler if
+ // needed
+ mPipTouchHandlerOptional.ifPresent((handler) -> handler.init());
}
@ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index efc55c4fbe31..9ddeb2fc6e1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -44,7 +44,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
-import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
+import com.android.wm.shell.startingsurface.StartingSurface;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -110,7 +110,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>();
private final Object mLock = new Object();
- private final StartingSurfaceDrawer mStartingSurfaceDrawer;
+ private StartingSurface mStartingSurface;
/**
* In charge of showing size compat UI. Can be {@code null} if device doesn't support size
@@ -120,23 +120,19 @@ public class ShellTaskOrganizer extends TaskOrganizer {
private final SizeCompatUIController mSizeCompatUI;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
- this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
- new StartingSurfaceDrawer(context, mainExecutor));
+ this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
SizeCompatUIController sizeCompatUI) {
- this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
- new StartingSurfaceDrawer(context, mainExecutor));
+ this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
}
@VisibleForTesting
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
- Context context, @Nullable SizeCompatUIController sizeCompatUI,
- StartingSurfaceDrawer startingSurfaceDrawer) {
+ Context context, @Nullable SizeCompatUIController sizeCompatUI) {
super(taskOrganizerController, mainExecutor);
mSizeCompatUI = sizeCompatUI;
- mStartingSurfaceDrawer = startingSurfaceDrawer;
}
@Override
@@ -163,6 +159,15 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
/**
+ * @hide
+ */
+ public void initStartingSurface(StartingSurface startingSurface) {
+ synchronized (mLock) {
+ mStartingSurface = startingSurface;
+ }
+ }
+
+ /**
* Adds a listener for a specific task id.
*/
public void addListenerForTaskId(TaskListener listener, int taskId) {
@@ -254,17 +259,23 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
- mStartingSurfaceDrawer.addStartingWindow(info, appToken);
+ if (mStartingSurface != null) {
+ mStartingSurface.addStartingWindow(info, appToken);
+ }
}
@Override
public void removeStartingWindow(int taskId) {
- mStartingSurfaceDrawer.removeStartingWindow(taskId);
+ if (mStartingSurface != null) {
+ mStartingSurface.removeStartingWindow(taskId);
+ }
}
@Override
public void copySplashScreenView(int taskId) {
- mStartingSurfaceDrawer.copySplashScreenView(taskId);
+ if (mStartingSurface != null) {
+ mStartingSurface.copySplashScreenView(taskId);
+ }
}
@Override
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/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 6f5f2eb5723c..aab2334f8255 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -234,8 +234,8 @@ public class DragAndDropPolicy {
final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
mStarter.startShortcut(packageName, id, stage, position, opts, user);
} else {
- mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
- opts);
+ mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
+ mContext, null, stage, position, opts);
}
}
@@ -295,7 +295,8 @@ public class DragAndDropPolicy {
@Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
@StagePosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+ void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
+ @StageType int stage, @StagePosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
@@ -336,10 +337,11 @@ public class DragAndDropPolicy {
}
@Override
- public void startIntent(PendingIntent intent, int stage, int position,
+ public void startIntent(PendingIntent intent, Context context,
+ @Nullable Intent fillInIntent, int stage, int position,
@Nullable Bundle options) {
try {
- intent.send(null, 0, null, null, null, null, options);
+ intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
index 05526018d73f..f4c0f9384705 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
@@ -52,6 +52,9 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
private final SyncTransactionQueue mSyncQueue;
private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
+ // TODO(shell-transitions): Remove when switched to shell-transitions.
+ private final SparseArray<Point> mPositionByTaskId = new SparseArray<>();
+
RunningTaskInfo mPrimary;
RunningTaskInfo mSecondary;
SurfaceControl mPrimarySurface;
@@ -167,6 +170,7 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
synchronized (this) {
+ mPositionByTaskId.remove(taskInfo.taskId);
if (taskInfo.hasParentTask()) {
mLeashByTaskId.remove(taskInfo.taskId);
return;
@@ -200,16 +204,24 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
}
synchronized (this) {
if (taskInfo.hasParentTask()) {
+ // changed messages are noisy since it reports on every ensureVisibility. This
+ // conflicts with legacy app-transitions which "swaps" the position to a
+ // leash. For now, only update when position actually changes to avoid
+ // poorly-timed duplicate calls.
+ if (taskInfo.positionInParent.equals(mPositionByTaskId.get(taskInfo.taskId))) {
+ return;
+ }
handleChildTaskChanged(taskInfo);
- return;
+ } else {
+ handleTaskInfoChanged(taskInfo);
}
-
- handleTaskInfoChanged(taskInfo);
+ mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
}
}
private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
mLeashByTaskId.put(taskInfo.taskId, leash);
+ mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
}
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/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 843e27f1b651..7ed7fd0096bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -172,6 +172,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
};
private ActivityManager.RunningTaskInfo mTaskInfo;
+ // To handle the edge case that onTaskInfoChanged callback is received during the entering
+ // PiP transition, where we do not want to intercept the transition but still want to apply the
+ // changed RunningTaskInfo when it finishes.
+ private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
private State mState = State.UNDEFINED;
@@ -520,12 +524,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipTransitionController.sendOnPipTransitionStarted(direction);
}
- private void sendOnPipTransitionFinished(
+ @VisibleForTesting
+ void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERED_PIP;
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
+ // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
+ if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
+ onTaskInfoChanged(mDeferredTaskInfo);
+ mDeferredTaskInfo = null;
+ }
}
private void sendOnPipTransitionCancelled(
@@ -567,6 +577,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
+ if (mState != State.ENTERED_PIP) {
+ Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+ mDeferredTaskInfo = info;
+ return;
+ }
mPipBoundsState.setLastPipComponentName(info.topActivity);
mPipBoundsState.setOverrideMinSize(
mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index d9a7bdb2eca6..9ee6a221c80c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -87,7 +87,7 @@ public class PipDismissTargetHandler {
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
// Allow dragging the PIP to a location to close it
- private final boolean mEnableDismissDragToEdge;
+ private boolean mEnableDismissDragToEdge;
private int mDismissAreaHeight;
@@ -104,67 +104,66 @@ public class PipDismissTargetHandler {
mMotionHelper = motionHelper;
mMainExecutor = mainExecutor;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
- Resources res = context.getResources();
+ public void init() {
+ Resources res = mContext.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
- mMainExecutor.execute(() -> {
- mTargetView = new DismissCircleView(context);
- mTargetViewContainer = new FrameLayout(context);
- mTargetViewContainer.setBackgroundDrawable(
- context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
- mTargetViewContainer.setClipChildren(false);
- mTargetViewContainer.addView(mTargetView);
-
- mMagnetizedPip = mMotionHelper.getMagnetizedPip();
- mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
- updateMagneticTargetSize();
-
- mMagnetizedPip.setAnimateStuckToTarget(
- (target, velX, velY, flung, after) -> {
- if (mEnableDismissDragToEdge) {
- mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung,
- after);
- }
- return Unit.INSTANCE;
- });
- mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
- @Override
- public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- // Show the dismiss target, in case the initial touch event occurred within
- // the magnetic field radius.
+ mTargetView = new DismissCircleView(mContext);
+ mTargetViewContainer = new FrameLayout(mContext);
+ mTargetViewContainer.setBackgroundDrawable(
+ mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition));
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTargetView);
+
+ mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+ mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+ updateMagneticTargetSize();
+
+ mMagnetizedPip.setAnimateStuckToTarget(
+ (target, velX, velY, flung, after) -> {
if (mEnableDismissDragToEdge) {
- showDismissTargetMaybe();
+ mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
}
+ return Unit.INSTANCE;
+ });
+ mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+ @Override
+ public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ // Show the dismiss target, in case the initial touch event occurred within
+ // the magnetic field radius.
+ if (mEnableDismissDragToEdge) {
+ showDismissTargetMaybe();
}
+ }
- @Override
- public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
- float velX, float velY, boolean wasFlungOut) {
- if (wasFlungOut) {
- mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
- hideDismissTargetMaybe();
- } else {
- mMotionHelper.setSpringingToTouch(true);
- }
+ @Override
+ public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+ float velX, float velY, boolean wasFlungOut) {
+ if (wasFlungOut) {
+ mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
+ hideDismissTargetMaybe();
+ } else {
+ mMotionHelper.setSpringingToTouch(true);
}
+ }
- @Override
- public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mMainExecutor.executeDelayed(() -> {
- mMotionHelper.notifyDismissalPending();
- mMotionHelper.animateDismiss();
- hideDismissTargetMaybe();
-
- mPipUiEventLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
- }, 0);
- }
- });
+ @Override
+ public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ mMainExecutor.executeDelayed(() -> {
+ mMotionHelper.notifyDismissalPending();
+ mMotionHelper.animateDismiss();
+ hideDismissTargetMaybe();
- mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
+ }, 0);
+ }
});
+
+ mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index eae8945ce6be..d742aa688fe7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -102,7 +102,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
* using physics animations.
*/
- private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;
+ private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;
private MagnetizedObject<Rect> mMagnetizedPip;
@@ -171,7 +171,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController,
- FloatingContentCoordinator floatingContentCoordinator, ShellExecutor mainExecutor) {
+ FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsState = pipBoundsState;
@@ -179,15 +179,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
- mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
- mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
-
- // Need to get the shell main thread sf vsync animation handler
- mainExecutor.execute(() -> {
- mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
- mSfAnimationHandlerThreadLocal.get());
- });
-
mResizePipUpdateListener = (target, values) -> {
if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
@@ -196,6 +187,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
};
}
+ public void init() {
+ // Note: Needs to get the shell main thread sf vsync animation handler
+ mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
+ mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+ mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
+ mSfAnimationHandlerThreadLocal.get());
+ }
+
@NonNull
@Override
public Rect getFloatingBoundsOnScreen() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 6a24ebf244d8..c726c012c6a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -134,8 +134,10 @@ public class PipResizeGestureHandler {
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
+ }
- context.getDisplay().getRealSize(mMaxSize);
+ public void init() {
+ mContext.getDisplay().getRealSize(mMaxSize);
reloadResources();
mEnablePinchResize = DeviceConfig.getBoolean(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 5e23281b3438..543ecfcf1a33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -71,12 +71,13 @@ public class PipTouchHandler {
private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
// Allow PIP to resize to a slightly bigger state upon touch
- private final boolean mEnableResize;
+ private boolean mEnableResize;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
+ private final ShellExecutor mMainExecutor;
private PipResizeGestureHandler mPipResizeGestureHandler;
private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener;
@@ -166,16 +167,18 @@ public class PipTouchHandler {
ShellExecutor mainExecutor) {
// Initialize the Pip input consumer
mContext = context;
+ mMainExecutor = mainExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
+ mFloatingContentCoordinator = floatingContentCoordinator;
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), pipTransitionController,
- floatingContentCoordinator, mainExecutor);
+ floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, pipTaskOrganizer, this::getMovementBounds,
@@ -199,22 +202,26 @@ public class PipTouchHandler {
},
menuController::hideMenu,
mainExecutor);
+ mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
+ mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
+ this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor);
+ }
- Resources res = context.getResources();
+ public void init() {
+ Resources res = mContext.getResources();
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
reloadResources();
- mFloatingContentCoordinator = floatingContentCoordinator;
- mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
- mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
- this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor);
+ mMotionHelper.init();
+ mPipResizeGestureHandler.init();
+ mPipDismissTargetHandler.init();
mEnableStash = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_STASHING,
/* defaultValue = */ true);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mainExecutor,
+ mMainExecutor,
properties -> {
if (properties.getKeyset().contains(PIP_STASHING)) {
mEnableStash = properties.getBoolean(
@@ -226,7 +233,7 @@ public class PipTouchHandler {
PIP_STASH_MINIMUM_VELOCITY_THRESHOLD,
DEFAULT_STASH_VELOCITY_THRESHOLD);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mainExecutor,
+ mMainExecutor,
properties -> {
if (properties.getKeyset().contains(PIP_STASH_MINIMUM_VELOCITY_THRESHOLD)) {
mStashVelocityThreshold = properties.getFloat(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index 32f3648be19a..c6d994ecde8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -280,7 +280,7 @@ class SizeCompatUILayout {
: stableBounds.right - taskBounds.left - mButtonSize;
final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize;
- mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
+ updateSurfacePosition(leash, positionX, positionY);
}
void updateHintSurfacePosition() {
@@ -303,7 +303,16 @@ class SizeCompatUILayout {
final int positionY =
stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
- mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
+ updateSurfacePosition(leash, positionX, positionY);
+ }
+
+ private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ t.setPosition(leash, positionX, positionY);
+ // The size compat UI should be the topmost child of the Task in case there can be more
+ // than one children.
+ t.setLayer(leash, Integer.MAX_VALUE);
+ });
}
int getDisplayId() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7ca569349633..25a84bd46484 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -19,18 +19,17 @@ package com.android.wm.shell.splitscreen;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
-import java.io.PrintWriter;
-
/**
* Interface to engage split-screen feature.
* TODO: Figure out which of these are actually needed outside of the Shell
@@ -87,7 +86,7 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
/** Callback interface for listening to changes in a split-screen stage. */
interface SplitScreenListener {
void onStagePositionChanged(@StageType int stage, @StagePosition int position);
- void onTaskStageChanged(int taskId, @StageType int stage);
+ void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
}
/** @return {@code true} if split-screen is currently visible. */
@@ -118,6 +117,7 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
@StageType int stage, @StagePosition int position, @Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
@StagePosition int position, @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+ void startIntent(PendingIntent intent, Context context,
+ @Nullable Intent fillInIntent, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 11548adaf5d1..bb6f6f259a1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -30,6 +30,7 @@ import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
@@ -171,12 +172,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
}
- public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ public void startIntent(PendingIntent intent, Context context,
+ Intent fillInIntent, @SplitScreen.StageType int stage,
@SplitScreen.StagePosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
- intent.send(null, 0, null, null, null, null, options);
+ intent.send(context, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -348,10 +350,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
@Override
- public void startIntent(PendingIntent intent, int stage, int position,
- @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
+ int stage, int position, @Nullable Bundle options) {
mMainExecutor.execute(() -> {
- SplitScreenController.this.startIntent(intent, stage, position, options);
+ SplitScreenController.this.startIntent(intent, context, fillInIntent, stage,
+ position, options);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 22c97515ad76..b180bb52e3e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -225,7 +225,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
private void onStageChildTaskStatusChanged(
- StageListenerImpl stageListener, int taskId, boolean present) {
+ StageListenerImpl stageListener, int taskId, boolean present, boolean visible) {
int stage;
if (present) {
@@ -236,7 +236,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onTaskStageChanged(taskId, stage);
+ mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
}
}
@@ -362,8 +362,9 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
- final boolean mainStageToTop = bottomOrRight
- && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+ final boolean mainStageToTop =
+ bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT
+ : mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT;
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
}
@@ -494,8 +495,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
}
@Override
- public void onChildTaskStatusChanged(int taskId, boolean present) {
- StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+ public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) {
+ StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 10c742b69578..b8cdc4ab4d75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,7 +58,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
public interface StageListenerCallbacks {
void onRootTaskAppeared();
void onStatusChanged(boolean visible, boolean hasChildren);
- void onChildTaskStatusChanged(int taskId, boolean present);
+ void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
void onRootTaskVanished();
}
private final StageListenerCallbacks mCallbacks;
@@ -88,7 +88,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
mChildrenLeashes.put(taskId, leash);
mChildrenTaskInfo.put(taskId, taskInfo);
updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
- mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
+ mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -105,6 +105,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
updateChildTaskSurface(
taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
+ mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
+ taskInfo.isVisible);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -123,7 +125,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
sendStatusChanged();
- mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
+ mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -152,7 +154,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
@SplitScreen.StageType int stage) {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+ int taskId = mChildrenTaskInfo.keyAt(i);
+ listener.onTaskStageChanged(taskId, stage,
+ mChildrenTaskInfo.get(taskId).isVisible);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
new file mode 100644
index 000000000000..2c4ceffcb8f5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -0,0 +1,39 @@
+/*
+ * 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.wm.shell.startingsurface;
+
+import android.os.IBinder;
+import android.window.StartingWindowInfo;
+
+/**
+ * Interface to engage starting window feature.
+ */
+public interface StartingSurface {
+ /**
+ * Called when a task need a starting window.
+ */
+ void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken);
+ /**
+ * Called when the content of a task is ready to show, starting window can be removed.
+ */
+ void removeStartingWindow(int taskId);
+ /**
+ * Called when the Task wants to copy the splash screen.
+ * @param taskId
+ */
+ void copySplashScreenView(int taskId);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 5332291fd1bd..814407164750 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -16,15 +16,9 @@
package com.android.wm.shell.startingsurface;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.res.Configuration.EMPTY;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -37,7 +31,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -46,7 +39,6 @@ import android.view.WindowManager;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
-import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
import com.android.internal.R;
@@ -56,19 +48,13 @@ import com.android.wm.shell.common.ShellExecutor;
import java.util.function.Consumer;
/**
- * Implementation to draw the starting window to an application, and remove the starting window
- * until the application displays its own window.
- *
- * When receive {@link TaskOrganizer#addStartingWindow} callback, use this class to create a
- * starting window and attached to the Task, then when the Task want to remove the starting window,
- * the TaskOrganizer will receive {@link TaskOrganizer#removeStartingWindow} callback then use this
- * class to remove the starting window of the Task.
+ * A class which able to draw splash screen or snapshot as the starting window for a task.
* @hide
*/
public class StartingSurfaceDrawer {
static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
- static final boolean DEBUG_SPLASH_SCREEN = false;
- static final boolean DEBUG_TASK_SNAPSHOT = false;
+ static final boolean DEBUG_SPLASH_SCREEN = StartingWindowController.DEBUG_SPLASH_SCREEN;
+ static final boolean DEBUG_TASK_SNAPSHOT = StartingWindowController.DEBUG_TASK_SNAPSHOT;
private final Context mContext;
private final DisplayManager mDisplayManager;
@@ -107,106 +93,10 @@ public class StartingSurfaceDrawer {
return context.createDisplayContext(targetDisplay);
}
- private static class PreferredStartingTypeHelper {
- private static final int STARTING_TYPE_NO = 0;
- private static final int STARTING_TYPE_SPLASH_SCREEN = 1;
- private static final int STARTING_TYPE_SNAPSHOT = 2;
-
- TaskSnapshot mSnapshot;
- int mPreferredType;
-
- PreferredStartingTypeHelper(StartingWindowInfo taskInfo) {
- final int parameter = taskInfo.startingWindowTypeParameter;
- final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
- final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
- final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
- final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
- final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- mPreferredType = preferredStartingWindowType(taskInfo, newTask, taskSwitch,
- processRunning, allowTaskSnapshot, activityCreated);
- }
-
- // reference from ActivityRecord#getStartingWindowType
- private int preferredStartingWindowType(StartingWindowInfo windowInfo,
- boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated) {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
- + " taskSwitch " + taskSwitch
- + " processRunning " + processRunning
- + " allowTaskSnapshot " + allowTaskSnapshot
- + " activityCreated " + activityCreated);
- }
-
- if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
- return STARTING_TYPE_SPLASH_SCREEN;
- } else if (taskSwitch && allowTaskSnapshot) {
- final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
- if (isSnapshotCompatible(windowInfo, snapshot)) {
- return STARTING_TYPE_SNAPSHOT;
- }
- if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
- return STARTING_TYPE_SPLASH_SCREEN;
- }
- return STARTING_TYPE_NO;
- } else {
- return STARTING_TYPE_NO;
- }
- }
-
- /**
- * Returns {@code true} if the task snapshot is compatible with this activity (at least the
- * rotation must be the same).
- */
- private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) {
- if (snapshot == null) {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
- }
- return false;
- }
-
- final int taskRotation = windowInfo.taskInfo.configuration
- .windowConfiguration.getRotation();
- final int snapshotRotation = snapshot.getRotation();
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
- + " snapshot " + snapshotRotation);
- }
- return taskRotation == snapshotRotation;
- }
-
- private TaskSnapshot getTaskSnapshot(int taskId) {
- if (mSnapshot != null) {
- return mSnapshot;
- }
- try {
- mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
- false/* isLowResolution */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
- return null;
- }
- return mSnapshot;
- }
- }
-
/**
- * Called when a task need a starting window.
+ * Called when a task need a splash screen starting window.
*/
- public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
- final PreferredStartingTypeHelper helper =
- new PreferredStartingTypeHelper(windowInfo);
- if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SPLASH_SCREEN) {
- addSplashScreenStartingWindow(windowInfo, appToken);
- } else if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = helper.mSnapshot;
- makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
- }
- // If prefer don't show, then don't show!
- }
-
- private void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+ public void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
final RunningTaskInfo taskInfo = windowInfo.taskInfo;
final ActivityInfo activityInfo = taskInfo.topActivityInfo;
if (activityInfo == null) {
@@ -378,8 +268,8 @@ public class StartingSurfaceDrawer {
/**
* Called when a task need a snapshot starting window.
*/
- private void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo,
- IBinder appToken, TaskSnapshot snapshot) {
+ void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken,
+ TaskSnapshot snapshot) {
final int taskId = startingWindowInfo.taskInfo.taskId;
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
new file mode 100644
index 000000000000..73bf8ac90c29
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
+
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.window.StartingWindowInfo;
+import android.window.TaskOrganizer;
+import android.window.TaskSnapshot;
+
+import com.android.wm.shell.common.ShellExecutor;
+
+/**
+ * Implementation to draw the starting window to an application, and remove the starting window
+ * until the application displays its own window.
+ *
+ * When receive {@link TaskOrganizer#addStartingWindow} callback, use this class to create a
+ * starting window and attached to the Task, then when the Task want to remove the starting window,
+ * the TaskOrganizer will receive {@link TaskOrganizer#removeStartingWindow} callback then use this
+ * class to remove the starting window of the Task.
+ * @hide
+ */
+public class StartingWindowController {
+ private static final String TAG = StartingWindowController.class.getSimpleName();
+ static final boolean DEBUG_SPLASH_SCREEN = false;
+ static final boolean DEBUG_TASK_SNAPSHOT = false;
+
+ private final StartingSurfaceDrawer mStartingSurfaceDrawer;
+ private final StartingTypeChecker mStartingTypeChecker = new StartingTypeChecker();
+ private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
+
+ public StartingWindowController(Context context, ShellExecutor mainExecutor) {
+ mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor);
+ }
+
+ /**
+ * Provide the implementation for Shell Module.
+ */
+ public StartingSurface asStartingSurface() {
+ return mImpl;
+ }
+
+ private static class StartingTypeChecker {
+ TaskSnapshot mSnapshot;
+
+ StartingTypeChecker() { }
+
+ private void reset() {
+ mSnapshot = null;
+ }
+
+ private @StartingWindowInfo.StartingWindowType int
+ estimateStartingWindowType(StartingWindowInfo windowInfo) {
+ reset();
+ final int parameter = windowInfo.startingWindowTypeParameter;
+ final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
+ final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
+ final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
+ final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
+ final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
+ return estimateStartingWindowType(windowInfo, newTask, taskSwitch,
+ processRunning, allowTaskSnapshot, activityCreated);
+ }
+
+ // reference from ActivityRecord#getStartingWindowType
+ private int estimateStartingWindowType(StartingWindowInfo windowInfo,
+ boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated) {
+ if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+ Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
+ + " taskSwitch " + taskSwitch
+ + " processRunning " + processRunning
+ + " allowTaskSnapshot " + allowTaskSnapshot
+ + " activityCreated " + activityCreated);
+ }
+ if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ }
+ if (taskSwitch && allowTaskSnapshot) {
+ final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
+ if (isSnapshotCompatible(windowInfo, snapshot)) {
+ return STARTING_WINDOW_TYPE_SNAPSHOT;
+ }
+ if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ }
+ }
+ return STARTING_WINDOW_TYPE_NONE;
+ }
+
+ /**
+ * Returns {@code true} if the task snapshot is compatible with this activity (at least the
+ * rotation must be the same).
+ */
+ private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) {
+ if (snapshot == null) {
+ if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+ Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
+ }
+ return false;
+ }
+
+ final int taskRotation = windowInfo.taskInfo.configuration
+ .windowConfiguration.getRotation();
+ final int snapshotRotation = snapshot.getRotation();
+ if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
+ Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
+ + " snapshot " + snapshotRotation);
+ }
+ return taskRotation == snapshotRotation;
+ }
+
+ private TaskSnapshot getTaskSnapshot(int taskId) {
+ if (mSnapshot != null) {
+ return mSnapshot;
+ }
+ try {
+ mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
+ false/* isLowResolution */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
+ return null;
+ }
+ return mSnapshot;
+ }
+ }
+
+ /**
+ * Called when a task need a starting window.
+ */
+ void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+ final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
+ if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
+ } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
+ final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
+ }
+ // If prefer don't show, then don't show!
+ }
+
+ void copySplashScreenView(int taskId) {
+ mStartingSurfaceDrawer.copySplashScreenView(taskId);
+ }
+
+ /**
+ * Called when the content of a task is ready to show, starting window can be removed.
+ */
+ void removeStartingWindow(int taskId) {
+ mStartingSurfaceDrawer.removeStartingWindow(taskId);
+ }
+
+ private class StartingSurfaceImpl implements StartingSurface {
+
+ @Override
+ public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+ StartingWindowController.this.addStartingWindow(windowInfo, appToken);
+ }
+
+ @Override
+ public void removeStartingWindow(int taskId) {
+ StartingWindowController.this.removeStartingWindow(taskId);
+ }
+
+ @Override
+ public void copySplashScreenView(int taskId) {
+ StartingWindowController.this.copySplashScreenView(taskId);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 2182ee5590e1..629ff0db6b4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -97,8 +97,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- // Don't animate anything with an animating parent
- if (change.getParent() != null) continue;
+ // Don't animate anything that isn't independent.
+ if (!TransitionInfo.isIndependent(change, info)) continue;
Animation a = loadAnimation(info.getType(), change);
if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 89eee67bf5af..677db10d07a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -214,8 +214,8 @@ public class Transitions {
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
- // Don't move anything with an animating parent
- if (change.getParent() != null) {
+ // Don't move anything that isn't independent within its parents
+ if (!TransitionInfo.isIndependent(change, info)) {
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
t.show(leash);
t.setMatrix(leash, 1, 0, 0, 1);
@@ -225,9 +225,13 @@ public class Transitions {
continue;
}
- t.reparent(leash, info.getRootLeash());
- t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
- change.getStartAbsBounds().top - info.getRootOffset().y);
+ boolean hasParent = change.getParent() != null;
+
+ if (!hasParent) {
+ t.reparent(leash, info.getRootLeash());
+ t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
+ change.getStartAbsBounds().top - info.getRootOffset().y);
+ }
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
t.show(leash);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index a0e9f43218f2..06b492dcb409 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -53,7 +53,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
-import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import org.junit.Before;
import org.junit.Test;
@@ -79,8 +78,6 @@ public class ShellTaskOrganizerTests {
private Context mContext;
@Mock
private SizeCompatUIController mSizeCompatUI;
- @Mock
- private StartingSurfaceDrawer mStartingSurfaceDrawer;
ShellTaskOrganizer mOrganizer;
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
@@ -116,7 +113,7 @@ public class ShellTaskOrganizerTests {
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mSizeCompatUI, mStartingSurfaceDrawer));
+ mSizeCompatUI));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 19ecc49513e5..c1c4c6dd08d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -205,7 +205,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -217,12 +217,12 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -234,12 +234,12 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -251,7 +251,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -263,7 +263,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -276,13 +276,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -295,13 +295,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(),
+ verify(mSplitScreenStarter).startIntent(any(), any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
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..195b701a1c26 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
@@ -16,6 +16,8 @@
package com.android.wm.shell.pip;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,7 +45,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 +126,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 +154,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),
@@ -163,11 +164,29 @@ public class PipTaskOrganizerTest extends ShellTestCase {
}
@Test
- public void onTaskInfoChanged_updatesAspectRatioIfChanged() {
+ public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() {
+ final Rational startAspectRatio = new Rational(2, 1);
+ final Rational newAspectRatio = new Rational(1, 2);
+ mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ createPipParams(startAspectRatio)), null /* leash */);
+
+ // It is in entering transition, should defer onTaskInfoChanged callback in this case.
+ mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ createPipParams(newAspectRatio)));
+ assertEquals(startAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
+
+ // Once the entering transition finishes, the new aspect ratio applies in a deferred manner
+ mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
+ }
+
+ @Test
+ public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), null /* leash */);
+ mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
createPipParams(newAspectRatio)));
@@ -176,9 +195,10 @@ public class PipTaskOrganizerTest extends ShellTestCase {
}
@Test
- public void onTaskInfoChanged_updatesLastPipComponentName() {
+ public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), null /* leash */);
+ mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null)));
@@ -187,11 +207,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
}
@Test
- public void onTaskInfoChanged_updatesOverrideMinSize() {
+ public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), null /* leash */);
+ mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- 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/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 19930485047c..75ea4ac94257 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -106,6 +106,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
mMockPipTransitionController, mFloatingContentCoordinator, mPipUiEventLogger,
mMainExecutor);
+ mPipTouchHandler.init();
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index de7d6c74bb06..b9af9ce8895d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -125,7 +125,7 @@ public class StartingSurfaceDrawerTests {
final Handler mainLoop = new Handler(Looper.getMainLooper());
final StartingWindowInfo windowInfo =
createWindowInfo(taskId, android.R.style.Theme);
- mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder);
waitHandlerIdle(mainLoop);
verify(mStartingSurfaceDrawer).postAddWindow(
eq(taskId), eq(mBinder), any(), any(), any(), any());
@@ -143,7 +143,7 @@ public class StartingSurfaceDrawerTests {
final Handler mainLoop = new Handler(Looper.getMainLooper());
final StartingWindowInfo windowInfo =
createWindowInfo(taskId, 0);
- mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
+ mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder);
waitHandlerIdle(mainLoop);
verify(mStartingSurfaceDrawer).postAddWindow(
eq(taskId), eq(mBinder), any(), any(), any(), any());
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 9c743cea592a..76366fce2aea 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -156,7 +156,11 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_
std::move(loaded_idmap)));
}
-const std::string& ApkAssets::GetPath() const {
+std::optional<std::string_view> ApkAssets::GetPath() const {
+ return assets_provider_->GetPath();
+}
+
+const std::string& ApkAssets::GetDebugName() const {
return assets_provider_->GetDebugName();
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 36bde5ccf267..c0ef7be8b673 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -116,8 +116,10 @@ void AssetManager2::BuildDynamicRefTable() {
package_groups_.clear();
package_ids_.fill(0xff);
- // A mapping from apk assets path to the runtime package id of its first loaded package.
- std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+ // A mapping from path of apk assets that could be target packages of overlays to the runtime
+ // package id of its first loaded package. Overlays currently can only override resources in the
+ // first package in the target resource table.
+ std::unordered_map<std::string, uint8_t> target_assets_package_ids;
// Overlay resources are not directly referenced by an application so their resource ids
// can change throughout the application's lifetime. Assign overlay package ids last.
@@ -140,8 +142,8 @@ void AssetManager2::BuildDynamicRefTable() {
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
// The target package must precede the overlay package in the apk assets paths in order
// to take effect.
- auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
- if (iter == apk_assets_package_ids.end()) {
+ auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
+ if (iter == target_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
} else {
@@ -205,7 +207,10 @@ void AssetManager2::BuildDynamicRefTable() {
package_name, static_cast<uint8_t>(entry.package_id));
}
- apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
+ if (auto apk_assets_path = apk_assets->GetPath()) {
+ // Overlay target ApkAssets must have been created using path based load apis.
+ target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id));
+ }
}
}
@@ -227,7 +232,7 @@ void AssetManager2::DumpToLog() const {
std::string list;
for (const auto& apk_assets : apk_assets_) {
- base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str());
+ base::StringAppendF(&list, "%s,", apk_assets->GetDebugName().c_str());
}
LOG(INFO) << "ApkAssets: " << list;
@@ -383,8 +388,8 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
}
}
-std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
- std::set<std::string> non_system_overlays;
+std::set<const ApkAssets*> AssetManager2::GetNonSystemOverlays() const {
+ std::set<const ApkAssets*> non_system_overlays;
for (const PackageGroup& package_group : package_groups_) {
bool found_system_package = false;
for (const ConfiguredPackage& package : package_group.packages_) {
@@ -396,7 +401,7 @@ std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
if (!found_system_package) {
for (const ConfiguredOverlay& overlay : package_group.overlays_) {
- non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath());
+ non_system_overlays.insert(apk_assets_[overlay.cookie]);
}
}
}
@@ -408,7 +413,7 @@ base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceCon
bool exclude_system, bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
const auto non_system_overlays =
- (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+ (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>();
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
@@ -419,8 +424,8 @@ base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceCon
}
auto apk_assets = apk_assets_[package_group.cookies_[i]];
- if (exclude_system && apk_assets->IsOverlay()
- && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ if (exclude_system && apk_assets->IsOverlay() &&
+ non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
// Exclude overlays that target system resources.
continue;
}
@@ -439,7 +444,7 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
const auto non_system_overlays =
- (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+ (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>();
for (const PackageGroup& package_group : package_groups_) {
for (size_t i = 0; i < package_group.packages_.size(); i++) {
@@ -449,8 +454,8 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
}
auto apk_assets = apk_assets_[package_group.cookies_[i]];
- if (exclude_system && apk_assets->IsOverlay()
- && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ if (exclude_system && apk_assets->IsOverlay() &&
+ non_system_overlays.find(apk_assets) == non_system_overlays.end()) {
// Exclude overlays that target system resources.
continue;
}
@@ -491,7 +496,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con
AssetDir::FileInfo info;
info.setFileName(String8(name.data(), name.size()));
info.setFileType(type);
- info.setSourceName(String8(apk_assets->GetPath().c_str()));
+ info.setSourceName(String8(apk_assets->GetDebugName().c_str()));
files->add(info);
};
@@ -846,7 +851,7 @@ std::string AssetManager2::GetLastResourceResolution() const {
}
log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " ("
- << apk_assets_[step.cookie]->GetPath() << ")";
+ << apk_assets_[step.cookie]->GetDebugName() << ")";
if (!step.config_name.isEmpty()) {
log_stream << " -" << step.config_name;
}
@@ -1556,41 +1561,32 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
// Determine which ApkAssets are loaded in both theme AssetManagers.
- std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets();
+ const auto src_assets = o.asset_manager_->GetApkAssets();
for (size_t i = 0; i < src_assets.size(); i++) {
const ApkAssets* src_asset = src_assets[i];
- std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets();
+ const auto dest_assets = asset_manager_->GetApkAssets();
for (size_t j = 0; j < dest_assets.size(); j++) {
const ApkAssets* dest_asset = dest_assets[j];
+ if (src_asset != dest_asset) {
+ // ResourcesManager caches and reuses ApkAssets when the same apk must be present in
+ // multiple AssetManagers. Two ApkAssets point to the same version of the same resources
+ // if they are the same instance.
+ continue;
+ }
- // Map the runtime package of the source apk asset to the destination apk asset.
- if (src_asset->GetPath() == dest_asset->GetPath()) {
- const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages();
- const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages();
-
- SourceToDestinationRuntimePackageMap package_map;
-
- // The source and destination package should have the same number of packages loaded in
- // the same order.
- const size_t N = src_packages.size();
- CHECK(N == dest_packages.size())
- << " LoadedArsc " << src_asset->GetPath() << " differs number of packages.";
- for (size_t p = 0; p < N; p++) {
- auto& src_package = src_packages[p];
- auto& dest_package = dest_packages[p];
- CHECK(src_package->GetPackageName() == dest_package->GetPackageName())
- << " Package " << src_package->GetPackageName() << " differs in load order.";
-
- int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get());
- int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get());
- package_map[src_package_id] = dest_package_id;
- }
-
- src_to_dest_asset_cookies.insert(std::make_pair(i, j));
- src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
- break;
+ // Map the package ids of the asset in the source AssetManager to the package ids of the
+ // asset in th destination AssetManager.
+ SourceToDestinationRuntimePackageMap package_map;
+ for (const auto& loaded_package : src_asset->GetLoadedArsc()->GetPackages()) {
+ const int src_package_id = o.asset_manager_->GetAssignedPackageId(loaded_package.get());
+ const int dest_package_id = asset_manager_->GetAssignedPackageId(loaded_package.get());
+ package_map[src_package_id] = dest_package_id;
}
+
+ src_to_dest_asset_cookies.insert(std::make_pair(i, j));
+ src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
+ break;
}
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index f3c48f7f9fc8..0aaf0b359b60 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -261,6 +261,13 @@ std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const {
return entry.crc32;
}
+std::optional<std::string_view> ZipAssetsProvider::GetPath() const {
+ if (name_.GetPath() != nullptr) {
+ return *name_.GetPath();
+ }
+ return {};
+}
+
const std::string& ZipAssetsProvider::GetDebugName() const {
return name_.GetDebugName();
}
@@ -318,6 +325,10 @@ bool DirectoryAssetsProvider::ForEachFile(
return true;
}
+std::optional<std::string_view> DirectoryAssetsProvider::GetPath() const {
+ return dir_;
+}
+
const std::string& DirectoryAssetsProvider::GetDebugName() const {
return dir_;
}
@@ -336,13 +347,9 @@ MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& prima
std::unique_ptr<AssetsProvider>&& secondary)
: primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
- if (primary_->GetDebugName() == kEmptyDebugString) {
- debug_name_ = secondary_->GetDebugName();
- } else if (secondary_->GetDebugName() == kEmptyDebugString) {
- debug_name_ = primary_->GetDebugName();
- } else {
- debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
- }
+ debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
+ path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath()
+ : secondary_->GetPath();
}
std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
@@ -367,6 +374,10 @@ bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
}
+std::optional<std::string_view> MultiAssetsProvider::GetPath() const {
+ return path_;
+}
+
const std::string& MultiAssetsProvider::GetDebugName() const {
return debug_name_;
}
@@ -394,6 +405,10 @@ bool EmptyAssetsProvider::ForEachFile(
return true;
}
+std::optional<std::string_view> EmptyAssetsProvider::GetPath() const {
+ return {};
+}
+
const std::string& EmptyAssetsProvider::GetDebugName() const {
const static std::string kEmpty = kEmptyDebugString;
return kEmpty;
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index d0019ed6be30..6f88f41976cd 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -34,7 +34,6 @@ namespace android {
// Holds an APK.
class ApkAssets {
public:
-
// Creates an ApkAssets from a path on device.
static std::unique_ptr<ApkAssets> Load(const std::string& path,
package_property_t flags = 0U);
@@ -61,12 +60,11 @@ class ApkAssets {
static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path,
package_property_t flags = 0U);
- // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same.
- // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause
- // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly
- // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the
- // same asset should have the same pointer.
- const std::string& GetPath() const;
+ // Path to the contents of the ApkAssets on disk. The path could represent an APk, a directory,
+ // or some other file type.
+ std::optional<std::string_view> GetPath() const;
+
+ const std::string& GetDebugName() const;
const AssetsProvider* GetAssetsProvider() const {
return assets_provider_.get();
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 2255973f1039..119f531b8634 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -412,7 +412,7 @@ class AssetManager2 {
void RebuildFilterList();
// Retrieves the APK paths of overlays that overlay non-system packages.
- std::set<std::string> GetNonSystemOverlayPaths() const;
+ std::set<const ApkAssets*> GetNonSystemOverlays() const;
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 6f16ff453905..63bbdcc9698d 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -48,6 +48,10 @@ struct AssetsProvider {
virtual bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+ // Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an
+ // APk, a directory, or some other file type.
+ WARN_UNUSED virtual std::optional<std::string_view> GetPath() const = 0;
+
// Retrieves a name that represents the interface. This may or may not be the path of the
// interface source.
WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
@@ -85,9 +89,9 @@ struct ZipAssetsProvider : public AssetsProvider {
bool ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
+ WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
-
WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const;
~ZipAssetsProvider() override = default;
@@ -125,6 +129,7 @@ struct DirectoryAssetsProvider : public AssetsProvider {
bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
+ WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
@@ -149,6 +154,7 @@ struct MultiAssetsProvider : public AssetsProvider {
bool ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
+ WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
@@ -163,6 +169,7 @@ struct MultiAssetsProvider : public AssetsProvider {
std::unique_ptr<AssetsProvider> primary_;
std::unique_ptr<AssetsProvider> secondary_;
+ std::optional<std::string_view> path_;
std::string debug_name_;
};
@@ -173,6 +180,7 @@ struct EmptyAssetsProvider : public AssetsProvider {
bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
+ WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
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/RenderProperties.h b/libs/hwui/RenderProperties.h
index 609706e2e49d..5540e2da78d9 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -552,8 +552,8 @@ public:
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
- (mComputedFields.mNeedLayerForFunctors ||
- mLayerProperties.mImageFilter != nullptr ||
+ (mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
+ !mLayerProperties.getStretchEffect().isEmpty() ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 51cbc7592861..d4fd1053b17f 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -15,13 +15,195 @@
*/
#include "StretchEffect.h"
+#include <SkImageFilter.h>
+#include <SkRefCnt.h>
+#include <SkRuntimeEffect.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <include/effects/SkImageFilters.h>
+
+#include <memory>
namespace android::uirenderer {
-sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
- // TODO: Implement & Cache
- // Probably need to use mutable to achieve caching
- return nullptr;
+static const SkString stretchShader = SkString(R"(
+ uniform shader uContentTexture;
+
+ // multiplier to apply to scale effect
+ uniform float uMaxStretchIntensity;
+
+ // Maximum percentage to stretch beyond bounds of target
+ uniform float uStretchAffectedDist;
+
+ // Distance stretched as a function of the normalized overscroll times
+ // scale intensity
+ uniform float uDistanceStretchedX;
+ uniform float uDistanceStretchedY;
+ uniform float uDistDiffX;
+
+ // Difference between the peak stretch amount and overscroll amount normalized
+ uniform float uDistDiffY;
+
+ // Horizontal offset represented as a ratio of pixels divided by the target width
+ uniform float uScrollX;
+ // Vertical offset represented as a ratio of pixels divided by the target height
+ uniform float uScrollY;
+
+ // Normalized overscroll amount in the horizontal direction
+ uniform float uOverscrollX;
+
+ // Normalized overscroll amount in the vertical direction
+ uniform float uOverscrollY;
+ uniform float viewportWidth; // target height in pixels
+ uniform float viewportHeight; // target width in pixels
+
+ void computeOverscrollStart(
+ out float outPos,
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float distanceStretched
+ ) {
+ float offsetPos = uStretchAffectedDist - inPos;
+ float posBasedVariation = smoothstep(0., uStretchAffectedDist, offsetPos);
+ float stretchIntensity = overscroll * posBasedVariation;
+ outPos = distanceStretched - (offsetPos / (1. + stretchIntensity));
+ }
+
+ void computeOverscrollEnd(
+ out float outPos,
+ float inPos,
+ float overscroll,
+ float reverseStretchDist,
+ float uStretchAffectedDist,
+ float distanceStretched
+ ) {
+ float offsetPos = inPos - reverseStretchDist;
+ float posBasedVariation = (smoothstep(0., uStretchAffectedDist, offsetPos));
+ float stretchIntensity = (-overscroll) * posBasedVariation;
+ outPos = 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
+ }
+
+ void computeOverscroll(
+ out float outPos,
+ float inPos,
+ float overscroll,
+ float uStretchAffectedDist,
+ float distanceStretched,
+ float distanceDiff
+ ) {
+ if (overscroll > 0) {
+ if (inPos <= uStretchAffectedDist) {
+ computeOverscrollStart(
+ outPos,
+ inPos,
+ overscroll,
+ uStretchAffectedDist,
+ distanceStretched
+ );
+ } else if (inPos >= distanceStretched) {
+ outPos = distanceDiff + inPos;
+ }
+ }
+ if (overscroll < 0) {
+ float stretchAffectedDist = 1. - uStretchAffectedDist;
+ if (inPos >= stretchAffectedDist) {
+ computeOverscrollEnd(
+ outPos,
+ inPos,
+ overscroll,
+ stretchAffectedDist,
+ uStretchAffectedDist,
+ distanceStretched
+ );
+ } else if (inPos < stretchAffectedDist) {
+ outPos = -distanceDiff + inPos;
+ }
+ }
+ }
+
+ vec4 main(vec2 coord) {
+ // Normalize SKSL pixel coordinate into a unit vector
+ float inU = coord.x / viewportWidth;
+ float inV = coord.y / viewportHeight;
+ float outU;
+ float outV;
+ float stretchIntensity;
+ // Add the normalized scroll position within scrolling list
+ inU += uScrollX;
+ inV += uScrollY;
+ outU = inU;
+ outV = inV;
+ computeOverscroll(
+ outU,
+ inU,
+ uOverscrollX,
+ uStretchAffectedDist,
+ uDistanceStretchedX,
+ uDistDiffX
+ );
+ computeOverscroll(
+ outV,
+ inV,
+ uOverscrollY,
+ uStretchAffectedDist,
+ uDistanceStretchedY,
+ uDistDiffY
+ );
+ coord.x = outU * viewportWidth;
+ coord.y = outV * viewportHeight;
+ return sample(uContentTexture, coord);
+ })");
+
+static const float ZERO = 0.f;
+
+sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
+ if (isEmpty()) {
+ return nullptr;
+ }
+
+ if (mStretchFilter != nullptr) {
+ return mStretchFilter;
+ }
+
+ float distanceNotStretchedX = maxStretchAmount / stretchArea.width();
+ float distanceNotStretchedY = maxStretchAmount / stretchArea.height();
+ float normOverScrollDistX = mStretchDirection.x();
+ float normOverScrollDistY = mStretchDirection.y();
+ float distanceStretchedX = maxStretchAmount / (1 + abs(normOverScrollDistX));
+ float distanceStretchedY = maxStretchAmount / (1 + abs(normOverScrollDistY));
+ float diffX = distanceStretchedX - distanceNotStretchedX;
+ float diffY = distanceStretchedY - distanceNotStretchedY;
+ float viewportWidth = stretchArea.width();
+ float viewportHeight = stretchArea.height();
+
+ if (mBuilder == nullptr) {
+ mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
+ }
+
+ mBuilder->child("uContentTexture") = snapshotImage->makeShader(
+ SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
+ mBuilder->uniform("uStretchAffectedDist").set(&maxStretchAmount, 1);
+ mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
+ mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
+ mBuilder->uniform("uDistDiffX").set(&diffX, 1);
+ mBuilder->uniform("uDistDiffY").set(&diffY, 1);
+ mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
+ mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
+ mBuilder->uniform("uScrollX").set(&ZERO, 1);
+ mBuilder->uniform("uScrollY").set(&ZERO, 1);
+ mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
+ mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+
+ mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
+ SkRect{0, 0, viewportWidth, viewportHeight});
+
+ return mStretchFilter;
+}
+
+sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
+ const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
+ return instance.effect;
}
} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 7dfd6398765a..d2da06b31f68 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -18,9 +18,11 @@
#include "utils/MathUtils.h"
+#include <SkImage.h>
+#include <SkImageFilter.h>
#include <SkPoint.h>
#include <SkRect.h>
-#include <SkImageFilter.h>
+#include <SkRuntimeEffect.h>
namespace android::uirenderer {
@@ -31,15 +33,27 @@ public:
SmoothStep,
};
+ StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmount)
+ : stretchArea(area), maxStretchAmount(maxStretchAmount), mStretchDirection(direction) {}
+
+ StretchEffect() {}
+
bool isEmpty() const {
- return MathUtils::isZero(stretchDirection.x())
- && MathUtils::isZero(stretchDirection.y());
+ return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
}
void setEmpty() {
*this = StretchEffect{};
}
+ StretchEffect& operator=(const StretchEffect& other) {
+ this->stretchArea = other.stretchArea;
+ this->mStretchDirection = other.mStretchDirection;
+ this->mStretchFilter = nullptr;
+ this->maxStretchAmount = other.maxStretchAmount;
+ return *this;
+ }
+
void mergeWith(const StretchEffect& other) {
if (other.isEmpty()) {
return;
@@ -48,7 +62,7 @@ public:
*this = other;
return;
}
- stretchDirection += other.stretchDirection;
+ setStretchDirection(mStretchDirection + other.mStretchDirection);
if (isEmpty()) {
return setEmpty();
}
@@ -56,11 +70,23 @@ public:
maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
}
- sk_sp<SkImageFilter> getImageFilter() const;
+ sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
SkRect stretchArea {0, 0, 0, 0};
- SkVector stretchDirection {0, 0};
float maxStretchAmount = 0;
+
+ void setStretchDirection(const SkVector& direction) {
+ mStretchFilter = nullptr;
+ mStretchDirection = direction;
+ }
+
+ const SkVector getStretchDirection() const { return mStretchDirection; }
+
+private:
+ static sk_sp<SkRuntimeEffect> getStretchEffect();
+ mutable SkVector mStretchDirection{0, 0};
+ mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+ mutable sk_sp<SkImageFilter> mStretchFilter;
};
} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 5f60437b5cc7..fc7d0d181949 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -180,14 +180,13 @@ static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA j
}
static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
- jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+ jfloat left, jfloat top, jfloat right,
+ jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
+ StretchEffect effect =
+ StretchEffect(SkRect::MakeLTRB(left, top, right, bottom), {.fX = vX, .fY = vY}, max);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
- StretchEffect{
- .stretchArea = SkRect::MakeLTRB(left, top, right, bottom),
- .stretchDirection = {.fX = vX, .fY = vY},
- .maxStretchAmount = max
- });
+ effect);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
return true;
}
@@ -659,10 +658,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
return;
}
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
+ SkVector stretchDirection = effect->getStretchDirection();
env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
info.canvasContext.getFrameNumber(), area.left, area.top,
- area.right, area.bottom, effect->stretchDirection.fX,
- effect->stretchDirection.fY, effect->maxStretchAmount);
+ area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
+ effect->maxStretchAmount);
#endif
env->DeleteLocalRef(localref);
}
@@ -702,106 +702,110 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
const char* const kClassPathName = "android/graphics/RenderNode";
static const JNINativeMethod gMethods[] = {
-// ----------------------------------------------------------------------------
-// Regular JNI
-// ----------------------------------------------------------------------------
- { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
- { "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer },
- { "nOutput", "(J)V", (void*) android_view_RenderNode_output },
- { "nGetUsageSize", "(J)I", (void*) android_view_RenderNode_getUsageSize },
- { "nGetAllocatedSize", "(J)I", (void*) android_view_RenderNode_getAllocatedSize },
- { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
- { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators },
- { "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
-
-// ----------------------------------------------------------------------------
-// Critical JNI via @CriticalNative annotation in RenderNode.java
-// ----------------------------------------------------------------------------
- { "nDiscardDisplayList", "(J)V", (void*) android_view_RenderNode_discardDisplayList },
- { "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid },
- { "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType },
- { "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType },
- { "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint },
- { "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix },
- { "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix },
- { "nGetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_getAnimationMatrix },
- { "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds },
- { "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds },
- { "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
- { "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty },
- { "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards },
- { "nSetProjectionReceiver","(JZ)Z", (void*) android_view_RenderNode_setProjectionReceiver },
-
- { "nSetOutlineRoundRect", "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
- { "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
- { "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
- { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
- { "nClearStretch", "(J)Z", (void*) android_view_RenderNode_clearStretch },
- { "nStretch", "(JFFFFFFF)Z", (void*) android_view_RenderNode_stretch },
- { "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
- { "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor },
- { "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor },
- { "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor },
- { "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor },
- { "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
- { "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
-
- { "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
- { "nSetRenderEffect", "(JJ)Z", (void*) android_view_RenderNode_setRenderEffect },
- { "nSetHasOverlappingRendering", "(JZ)Z",
- (void*) android_view_RenderNode_setHasOverlappingRendering },
- { "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
- { "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation },
- { "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX },
- { "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY },
- { "nSetTranslationZ", "(JF)Z", (void*) android_view_RenderNode_setTranslationZ },
- { "nSetRotation", "(JF)Z", (void*) android_view_RenderNode_setRotation },
- { "nSetRotationX", "(JF)Z", (void*) android_view_RenderNode_setRotationX },
- { "nSetRotationY", "(JF)Z", (void*) android_view_RenderNode_setRotationY },
- { "nSetScaleX", "(JF)Z", (void*) android_view_RenderNode_setScaleX },
- { "nSetScaleY", "(JF)Z", (void*) android_view_RenderNode_setScaleY },
- { "nSetPivotX", "(JF)Z", (void*) android_view_RenderNode_setPivotX },
- { "nSetPivotY", "(JF)Z", (void*) android_view_RenderNode_setPivotY },
- { "nResetPivot", "(J)Z", (void*) android_view_RenderNode_resetPivot },
- { "nSetCameraDistance", "(JF)Z", (void*) android_view_RenderNode_setCameraDistance },
- { "nSetLeft", "(JI)Z", (void*) android_view_RenderNode_setLeft },
- { "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop },
- { "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight },
- { "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom },
- { "nGetLeft", "(J)I", (void*) android_view_RenderNode_getLeft },
- { "nGetTop", "(J)I", (void*) android_view_RenderNode_getTop },
- { "nGetRight", "(J)I", (void*) android_view_RenderNode_getRight },
- { "nGetBottom", "(J)I", (void*) android_view_RenderNode_getBottom },
- { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
- { "nOffsetLeftAndRight", "(JI)Z", (void*) android_view_RenderNode_offsetLeftAndRight },
- { "nOffsetTopAndBottom", "(JI)Z", (void*) android_view_RenderNode_offsetTopAndBottom },
-
- { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering },
- { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline },
- { "nGetAlpha", "(J)F", (void*) android_view_RenderNode_getAlpha },
- { "nGetCameraDistance", "(J)F", (void*) android_view_RenderNode_getCameraDistance },
- { "nGetScaleX", "(J)F", (void*) android_view_RenderNode_getScaleX },
- { "nGetScaleY", "(J)F", (void*) android_view_RenderNode_getScaleY },
- { "nGetElevation", "(J)F", (void*) android_view_RenderNode_getElevation },
- { "nGetTranslationX", "(J)F", (void*) android_view_RenderNode_getTranslationX },
- { "nGetTranslationY", "(J)F", (void*) android_view_RenderNode_getTranslationY },
- { "nGetTranslationZ", "(J)F", (void*) android_view_RenderNode_getTranslationZ },
- { "nGetRotation", "(J)F", (void*) android_view_RenderNode_getRotation },
- { "nGetRotationX", "(J)F", (void*) android_view_RenderNode_getRotationX },
- { "nGetRotationY", "(J)F", (void*) android_view_RenderNode_getRotationY },
- { "nIsPivotExplicitlySet", "(J)Z", (void*) android_view_RenderNode_isPivotExplicitlySet },
- { "nHasIdentityMatrix", "(J)Z", (void*) android_view_RenderNode_hasIdentityMatrix },
-
- { "nGetTransformMatrix", "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
- { "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
-
- { "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
- { "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
- { "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth },
- { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
- { "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
- { "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
- { "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId },
+ // ----------------------------------------------------------------------------
+ // Regular JNI
+ // ----------------------------------------------------------------------------
+ {"nCreate", "(Ljava/lang/String;)J", (void*)android_view_RenderNode_create},
+ {"nGetNativeFinalizer", "()J", (void*)android_view_RenderNode_getNativeFinalizer},
+ {"nOutput", "(J)V", (void*)android_view_RenderNode_output},
+ {"nGetUsageSize", "(J)I", (void*)android_view_RenderNode_getUsageSize},
+ {"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
+ {"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
+ {"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
+ {"nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V",
+ (void*)android_view_RenderNode_requestPositionUpdates},
+
+ // ----------------------------------------------------------------------------
+ // Critical JNI via @CriticalNative annotation in RenderNode.java
+ // ----------------------------------------------------------------------------
+ {"nDiscardDisplayList", "(J)V", (void*)android_view_RenderNode_discardDisplayList},
+ {"nIsValid", "(J)Z", (void*)android_view_RenderNode_isValid},
+ {"nSetLayerType", "(JI)Z", (void*)android_view_RenderNode_setLayerType},
+ {"nGetLayerType", "(J)I", (void*)android_view_RenderNode_getLayerType},
+ {"nSetLayerPaint", "(JJ)Z", (void*)android_view_RenderNode_setLayerPaint},
+ {"nSetStaticMatrix", "(JJ)Z", (void*)android_view_RenderNode_setStaticMatrix},
+ {"nSetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_setAnimationMatrix},
+ {"nGetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_getAnimationMatrix},
+ {"nSetClipToBounds", "(JZ)Z", (void*)android_view_RenderNode_setClipToBounds},
+ {"nGetClipToBounds", "(J)Z", (void*)android_view_RenderNode_getClipToBounds},
+ {"nSetClipBounds", "(JIIII)Z", (void*)android_view_RenderNode_setClipBounds},
+ {"nSetClipBoundsEmpty", "(J)Z", (void*)android_view_RenderNode_setClipBoundsEmpty},
+ {"nSetProjectBackwards", "(JZ)Z", (void*)android_view_RenderNode_setProjectBackwards},
+ {"nSetProjectionReceiver", "(JZ)Z", (void*)android_view_RenderNode_setProjectionReceiver},
+
+ {"nSetOutlineRoundRect", "(JIIIIFF)Z", (void*)android_view_RenderNode_setOutlineRoundRect},
+ {"nSetOutlinePath", "(JJF)Z", (void*)android_view_RenderNode_setOutlinePath},
+ {"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
+ {"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
+ {"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
+ {"nStretch", "(JFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
+ {"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
+ {"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
+ {"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},
+ {"nSetAmbientShadowColor", "(JI)Z", (void*)android_view_RenderNode_setAmbientShadowColor},
+ {"nGetAmbientShadowColor", "(J)I", (void*)android_view_RenderNode_getAmbientShadowColor},
+ {"nSetClipToOutline", "(JZ)Z", (void*)android_view_RenderNode_setClipToOutline},
+ {"nSetRevealClip", "(JZFFF)Z", (void*)android_view_RenderNode_setRevealClip},
+
+ {"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
+ {"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
+ {"nSetHasOverlappingRendering", "(JZ)Z",
+ (void*)android_view_RenderNode_setHasOverlappingRendering},
+ {"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
+ {"nSetElevation", "(JF)Z", (void*)android_view_RenderNode_setElevation},
+ {"nSetTranslationX", "(JF)Z", (void*)android_view_RenderNode_setTranslationX},
+ {"nSetTranslationY", "(JF)Z", (void*)android_view_RenderNode_setTranslationY},
+ {"nSetTranslationZ", "(JF)Z", (void*)android_view_RenderNode_setTranslationZ},
+ {"nSetRotation", "(JF)Z", (void*)android_view_RenderNode_setRotation},
+ {"nSetRotationX", "(JF)Z", (void*)android_view_RenderNode_setRotationX},
+ {"nSetRotationY", "(JF)Z", (void*)android_view_RenderNode_setRotationY},
+ {"nSetScaleX", "(JF)Z", (void*)android_view_RenderNode_setScaleX},
+ {"nSetScaleY", "(JF)Z", (void*)android_view_RenderNode_setScaleY},
+ {"nSetPivotX", "(JF)Z", (void*)android_view_RenderNode_setPivotX},
+ {"nSetPivotY", "(JF)Z", (void*)android_view_RenderNode_setPivotY},
+ {"nResetPivot", "(J)Z", (void*)android_view_RenderNode_resetPivot},
+ {"nSetCameraDistance", "(JF)Z", (void*)android_view_RenderNode_setCameraDistance},
+ {"nSetLeft", "(JI)Z", (void*)android_view_RenderNode_setLeft},
+ {"nSetTop", "(JI)Z", (void*)android_view_RenderNode_setTop},
+ {"nSetRight", "(JI)Z", (void*)android_view_RenderNode_setRight},
+ {"nSetBottom", "(JI)Z", (void*)android_view_RenderNode_setBottom},
+ {"nGetLeft", "(J)I", (void*)android_view_RenderNode_getLeft},
+ {"nGetTop", "(J)I", (void*)android_view_RenderNode_getTop},
+ {"nGetRight", "(J)I", (void*)android_view_RenderNode_getRight},
+ {"nGetBottom", "(J)I", (void*)android_view_RenderNode_getBottom},
+ {"nSetLeftTopRightBottom", "(JIIII)Z",
+ (void*)android_view_RenderNode_setLeftTopRightBottom},
+ {"nOffsetLeftAndRight", "(JI)Z", (void*)android_view_RenderNode_offsetLeftAndRight},
+ {"nOffsetTopAndBottom", "(JI)Z", (void*)android_view_RenderNode_offsetTopAndBottom},
+
+ {"nHasOverlappingRendering", "(J)Z",
+ (void*)android_view_RenderNode_hasOverlappingRendering},
+ {"nGetClipToOutline", "(J)Z", (void*)android_view_RenderNode_getClipToOutline},
+ {"nGetAlpha", "(J)F", (void*)android_view_RenderNode_getAlpha},
+ {"nGetCameraDistance", "(J)F", (void*)android_view_RenderNode_getCameraDistance},
+ {"nGetScaleX", "(J)F", (void*)android_view_RenderNode_getScaleX},
+ {"nGetScaleY", "(J)F", (void*)android_view_RenderNode_getScaleY},
+ {"nGetElevation", "(J)F", (void*)android_view_RenderNode_getElevation},
+ {"nGetTranslationX", "(J)F", (void*)android_view_RenderNode_getTranslationX},
+ {"nGetTranslationY", "(J)F", (void*)android_view_RenderNode_getTranslationY},
+ {"nGetTranslationZ", "(J)F", (void*)android_view_RenderNode_getTranslationZ},
+ {"nGetRotation", "(J)F", (void*)android_view_RenderNode_getRotation},
+ {"nGetRotationX", "(J)F", (void*)android_view_RenderNode_getRotationX},
+ {"nGetRotationY", "(J)F", (void*)android_view_RenderNode_getRotationY},
+ {"nIsPivotExplicitlySet", "(J)Z", (void*)android_view_RenderNode_isPivotExplicitlySet},
+ {"nHasIdentityMatrix", "(J)Z", (void*)android_view_RenderNode_hasIdentityMatrix},
+
+ {"nGetTransformMatrix", "(JJ)V", (void*)android_view_RenderNode_getTransformMatrix},
+ {"nGetInverseTransformMatrix", "(JJ)V",
+ (void*)android_view_RenderNode_getInverseTransformMatrix},
+
+ {"nGetPivotX", "(J)F", (void*)android_view_RenderNode_getPivotX},
+ {"nGetPivotY", "(J)F", (void*)android_view_RenderNode_getPivotY},
+ {"nGetWidth", "(J)I", (void*)android_view_RenderNode_getWidth},
+ {"nGetHeight", "(J)I", (void*)android_view_RenderNode_getHeight},
+ {"nSetAllowForceDark", "(JZ)Z", (void*)android_view_RenderNode_setAllowForceDark},
+ {"nGetAllowForceDark", "(J)Z", (void*)android_view_RenderNode_getAllowForceDark},
+ {"nGetUniqueId", "(J)J", (void*)android_view_RenderNode_getUniqueId},
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index c01021221f37..cb0ff8d871d4 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -169,8 +169,8 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
displayList->mProjectedOutline = nullptr;
}
-static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
- SkPaint* paint) {
+static bool layerNeedsPaint(const sk_sp<SkImage>& snapshotImage, const LayerProperties& properties,
+ float alphaMultiplier, SkPaint* paint) {
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
@@ -179,7 +179,8 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
- sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
+ sk_sp<SkImageFilter> stretchFilter =
+ properties.getStretchEffect().getImageFilter(snapshotImage);
sk_sp<SkImageFilter> filter;
if (imageFilter && stretchFilter) {
filter = SkImageFilters::Compose(
@@ -240,7 +241,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
if (renderNode->getLayerSurface() && mComposeLayer) {
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
SkPaint paint;
- layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+ sk_sp<SkImage> snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
+ layerNeedsPaint(snapshotImage, layerProperties, alphaMultiplier, &paint);
SkSamplingOptions sampling(SkFilterMode::kLinear);
// surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
@@ -254,8 +256,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
canvas->drawAnnotation(bounds, String8::format(
"SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
}
- canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
- bounds, sampling, &paint, SkCanvas::kStrict_SrcRectConstraint);
+ canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
+ SkCanvas::kStrict_SrcRectConstraint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
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/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index c9146b2fc2d1..3408ffda3f9d 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -17,7 +17,7 @@
#include "DrawFrameTask.h"
#include <utils/Log.h>
-#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
#include "../DeferredLayerUpdater.h"
#include "../DisplayList.h"
@@ -82,7 +82,8 @@ void DrawFrameTask::postAndWait() {
}
void DrawFrameTask::run() {
- ATRACE_NAME("DrawFrame");
+ const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
+ ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
bool canUnblockUiThread;
bool canDrawThisFrame;
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/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index ff9fd4187272..ca175b4853a6 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -195,6 +195,61 @@ public final class AudioMetadata {
@NonNull public static final Key<Integer> KEY_AUDIO_ENCODING =
createKey("audio-encoding", Integer.class);
+
+ /**
+ * A key representing the audio presentation id being decoded by a next generation
+ * audio decoder.
+ *
+ * An Integer value representing presentation id.
+ *
+ * @see AudioPresentation#getPresentationId()
+ */
+ @NonNull public static final Key<Integer> KEY_PRESENTATION_ID =
+ createKey("presentation-id", Integer.class);
+
+ /**
+ * A key representing the audio program id being decoded by a next generation
+ * audio decoder.
+ *
+ * An Integer value representing program id.
+ *
+ * @see AudioPresentation#getProgramId()
+ */
+ @NonNull public static final Key<Integer> KEY_PROGRAM_ID =
+ createKey("program-id", Integer.class);
+
+
+ /**
+ * A key representing the audio presentation content classifier being rendered
+ * by a next generation audio decoder.
+ *
+ * An Integer value representing presentation content classifier.
+ *
+ * @see AudioPresentation.ContentClassifier
+ * One of {@link AudioPresentation#CONTENT_UNKNOWN},
+ * {@link AudioPresentation#CONTENT_MAIN},
+ * {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS},
+ * {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED},
+ * {@link AudioPresentation#CONTENT_HEARING_IMPAIRED},
+ * {@link AudioPresentation#CONTENT_DIALOG},
+ * {@link AudioPresentation#CONTENT_COMMENTARY},
+ * {@link AudioPresentation#CONTENT_EMERGENCY},
+ * {@link AudioPresentation#CONTENT_VOICEOVER}.
+ */
+ @NonNull public static final Key<Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER =
+ createKey("presentation-content-classifier", Integer.class);
+
+ /**
+ * A key representing the audio presentation language being rendered by a next
+ * generation audio decoder.
+ *
+ * A String value representing ISO 639-2 (three letter code).
+ *
+ * @see AudioPresentation#getLocale()
+ */
+ @NonNull public static final Key<String> KEY_PRESENTATION_LANGUAGE =
+ createKey("presentation-language", String.class);
+
private Format() {} // delete constructor
}
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 894fbba6c983..47358be3e926 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -57,6 +57,64 @@ public final class AudioPresentation {
/** @hide */
@IntDef(
value = {
+ CONTENT_UNKNOWN,
+ CONTENT_MAIN,
+ CONTENT_MUSIC_AND_EFFECTS,
+ CONTENT_VISUALLY_IMPAIRED,
+ CONTENT_HEARING_IMPAIRED,
+ CONTENT_DIALOG,
+ CONTENT_COMMENTARY,
+ CONTENT_EMERGENCY,
+ CONTENT_VOICEOVER,
+ })
+
+ /**
+ * The ContentClassifier int definitions represent the AudioPresentation content
+ * classifier (as per TS 103 190-1 v1.2.1 4.3.3.8.1)
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ContentClassifier {}
+
+ /**
+ * Audio presentation classifier: Unknown.
+ */
+ public static final int CONTENT_UNKNOWN = -1;
+ /**
+ * Audio presentation classifier: Complete main.
+ */
+ public static final int CONTENT_MAIN = 0;
+ /**
+ * Audio presentation content classifier: Music and effects.
+ */
+ public static final int CONTENT_MUSIC_AND_EFFECTS = 1;
+ /**
+ * Audio presentation content classifier: Visually impaired.
+ */
+ public static final int CONTENT_VISUALLY_IMPAIRED = 2;
+ /**
+ * Audio presentation content classifier: Hearing impaired.
+ */
+ public static final int CONTENT_HEARING_IMPAIRED = 3;
+ /**
+ * Audio presentation content classifier: Dialog.
+ */
+ public static final int CONTENT_DIALOG = 4;
+ /**
+ * Audio presentation content classifier: Commentary.
+ */
+ public static final int CONTENT_COMMENTARY = 5;
+ /**
+ * Audio presentation content classifier: Emergency.
+ */
+ public static final int CONTENT_EMERGENCY = 6;
+ /**
+ * Audio presentation content classifier: Voice over.
+ */
+ public static final int CONTENT_VOICEOVER = 7;
+
+ /** @hide */
+ @IntDef(
+ value = {
MASTERING_NOT_INDICATED,
MASTERED_FOR_STEREO,
MASTERED_FOR_SURROUND,
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 04eaf07d7924..88731d25706c 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -929,12 +929,21 @@ public class AudioTrack extends PlayerBase
private final int mSyncId;
/**
+ * A special content id for {@link #TunerConfiguration(int, int)}
+ * indicating audio is delivered
+ * from an {@code AudioTrack} write, not tunneled from the tuner stack.
+ */
+ public static final int CONTENT_ID_NONE = 0;
+
+ /**
* Constructs a TunerConfiguration instance for use in {@link AudioTrack.Builder}
*
* @param contentId selects the audio stream to use.
* The contentId may be obtained from
- * {@link android.media.tv.tuner.filter.Filter#getId()}.
- * This is always a positive number.
+ * {@link android.media.tv.tuner.filter.Filter#getId()},
+ * such obtained id is always a positive number.
+ * If audio is to be delivered through an {@code AudioTrack} write
+ * then {@code CONTENT_ID_NONE} may be used.
* @param syncId selects the clock to use for synchronization
* of audio with other streams such as video.
* The syncId may be obtained from
@@ -943,10 +952,10 @@ public class AudioTrack extends PlayerBase
*/
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public TunerConfiguration(
- @IntRange(from = 1) int contentId, @IntRange(from = 1)int syncId) {
- if (contentId < 1) {
+ @IntRange(from = 0) int contentId, @IntRange(from = 1)int syncId) {
+ if (contentId < 0) {
throw new IllegalArgumentException(
- "contentId " + contentId + " must be positive");
+ "contentId " + contentId + " must be positive or CONTENT_ID_NONE");
}
if (syncId < 1) {
throw new IllegalArgumentException("syncId " + syncId + " must be positive");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index f5b204a4a908..5656dffe1d4a 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.GraphicBuffer;
@@ -24,6 +25,7 @@ import android.graphics.ImageFormat.Format;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.MultiResolutionImageReader;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -36,7 +38,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -134,7 +138,8 @@ public class ImageReader implements AutoCloseable {
// If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
// work, and is inscrutable anyway
return new ImageReader(width, height, format, maxImages,
- format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+ /*parent*/ null);
}
/**
@@ -250,18 +255,37 @@ public class ImageReader implements AutoCloseable {
// throw new IllegalArgumentException("The given format=" + Integer.toHexString(format)
// + " & usage=" + Long.toHexString(usage) + " is not supported");
// }
- return new ImageReader(width, height, format, maxImages, usage);
+ return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null);
}
+ /**
+ * @hide
+ */
+ public static @NonNull ImageReader newInstance(
+ @IntRange(from = 1) int width,
+ @IntRange(from = 1) int height,
+ @Format int format,
+ @IntRange(from = 1) int maxImages,
+ @NonNull MultiResolutionImageReader parent) {
+ // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
+ // work, and is inscrutable anyway
+ return new ImageReader(width, height, format, maxImages,
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN,
+ parent);
+ }
+
+
/**
* @hide
*/
- protected ImageReader(int width, int height, int format, int maxImages, long usage) {
+ protected ImageReader(int width, int height, int format, int maxImages, long usage,
+ MultiResolutionImageReader parent) {
mWidth = width;
mHeight = height;
mFormat = format;
mUsage = usage;
mMaxImages = maxImages;
+ mParent = parent;
if (width < 1 || height < 1) {
throw new IllegalArgumentException(
@@ -433,6 +457,9 @@ public class ImageReader implements AutoCloseable {
if (image != null) {
image.close();
}
+ if (mParent != null) {
+ mParent.flushOther(this);
+ }
}
}
@@ -584,12 +611,38 @@ public class ImageReader implements AutoCloseable {
}
if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
mListenerHandler = new ListenerHandler(looper);
+ mListenerExecutor = new HandlerExecutor(mListenerHandler);
}
- mListener = listener;
} else {
- mListener = null;
mListenerHandler = null;
+ mListenerExecutor = null;
}
+ mListener = listener;
+ }
+ }
+
+ /**
+ * Register a listener to be invoked when a new image becomes available
+ * from the ImageReader.
+ *
+ * @param listener
+ * The listener that will be run.
+ * @param executor
+ * The executor which will be used to invoke the listener.
+ * @throws IllegalArgumentException
+ * If no handler specified and the calling thread has no looper.
+ *
+ * @hide
+ */
+ public void setOnImageAvailableListenerWithExecutor(@NonNull OnImageAvailableListener listener,
+ @NonNull Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ }
+
+ synchronized (mListenerLock) {
+ mListenerExecutor = executor;
+ mListener = listener;
}
}
@@ -763,12 +816,27 @@ public class ImageReader implements AutoCloseable {
return;
}
- final Handler handler;
+ final Executor executor;
+ final OnImageAvailableListener listener;
synchronized (ir.mListenerLock) {
- handler = ir.mListenerHandler;
+ executor = ir.mListenerExecutor;
+ listener = ir.mListener;
}
- if (handler != null) {
- handler.sendEmptyMessage(0);
+ final boolean isReaderValid;
+ synchronized (ir.mCloseLock) {
+ isReaderValid = ir.mIsReaderValid;
+ }
+
+ // It's dangerous to fire onImageAvailable() callback when the ImageReader
+ // is being closed, as application could acquire next image in the
+ // onImageAvailable() callback.
+ if (executor != null && listener != null && isReaderValid) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.onImageAvailable(ir);
+ }
+ });
}
}
@@ -785,11 +853,16 @@ public class ImageReader implements AutoCloseable {
private final Object mCloseLock = new Object();
private boolean mIsReaderValid = false;
private OnImageAvailableListener mListener;
+ private Executor mListenerExecutor;
private ListenerHandler mListenerHandler;
// Keep track of the successfully acquired Images. This need to be thread safe as the images
// could be closed by different threads (e.g., application thread and GC thread).
private List<Image> mAcquiredImages = new CopyOnWriteArrayList<>();
+ // Applicable if this isn't a standalone ImageReader, but belongs to a
+ // MultiResolutionImageReader.
+ private final MultiResolutionImageReader mParent;
+
/**
* This field is used by native code, do not access or modify.
*/
@@ -802,23 +875,22 @@ public class ImageReader implements AutoCloseable {
public ListenerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
+ }
- @Override
- public void handleMessage(Message msg) {
- OnImageAvailableListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
+ /**
+ * An adapter {@link Executor} that posts all executed tasks onto the
+ * given {@link Handler}.
+ **/
+ private final class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(@NonNull Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
+ }
- // It's dangerous to fire onImageAvailable() callback when the ImageReader is being
- // closed, as application could acquire next image in the onImageAvailable() callback.
- boolean isReaderValid = false;
- synchronized (mCloseLock) {
- isReaderValid = mIsReaderValid;
- }
- if (listener != null && isReaderValid) {
- listener.onImageAvailable(ImageReader.this);
- }
+ @Override
+ public void execute(Runnable command) {
+ mHandler.post(command);
}
}
diff --git a/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java
index 5646dcdb6c9c..96b61d2eaf4d 100644
--- a/media/java/android/media/metrics/Event.java
+++ b/media/java/android/media/metrics/Event.java
@@ -17,22 +17,30 @@
package android.media.metrics;
import android.annotation.IntRange;
+import android.os.Bundle;
/**
* Abstract class for metrics events.
*/
public abstract class Event {
- private final long mTimeSinceCreatedMillis;
+ final long mTimeSinceCreatedMillis;
+ Bundle mExtras;
// hide default constructor
/* package */ Event() {
mTimeSinceCreatedMillis = MediaMetricsManager.INVALID_TIMESTAMP;
}
+ // TODO: remove
protected Event(long timeSinceCreatedMillis) {
mTimeSinceCreatedMillis = timeSinceCreatedMillis;
}
+ /* package */ Event(long timeSinceCreatedMillis, Bundle extras) {
+ mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ mExtras = extras;
+ }
+
/**
* Gets time since the corresponding instance is created in millisecond.
* @return the timestamp since the instance is created, or -1 if unknown.
@@ -41,4 +49,9 @@ public abstract class Event {
public long getTimeSinceCreatedMillis() {
return mTimeSinceCreatedMillis;
}
+
+ /** @hide */
+ public Bundle getExtras() {
+ return mExtras;
+ }
}
diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl
index 2cb2ab5c42b5..f2c0d44a00b1 100644
--- a/media/java/android/media/metrics/IMediaMetricsManager.aidl
+++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl
@@ -28,7 +28,8 @@ import android.media.metrics.TrackChangeEvent;
*/
interface IMediaMetricsManager {
void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId);
- String getSessionId(int userId);
+ String getPlaybackSessionId(int userId);
+ String getRecordingSessionId(int userId);
void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
diff --git a/media/java/android/media/metrics/LogSessionId.java b/media/java/android/media/metrics/LogSessionId.java
new file mode 100644
index 000000000000..7ddb259f7f28
--- /dev/null
+++ b/media/java/android/media/metrics/LogSessionId.java
@@ -0,0 +1,34 @@
+/*
+ * 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.media.metrics;
+
+/**
+ * An instances of this class represents the ID of a log session.
+ * @hide
+ */
+public class LogSessionId {
+ private final String mSessionId;
+
+ /* package */ LogSessionId(String id) {
+ mSessionId = id;
+ }
+
+ /** @hide */
+ public String getStringId() {
+ return mSessionId;
+ }
+}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index de780f672b28..9710e88cf24b 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -94,7 +94,7 @@ public class MediaMetricsManager {
@NonNull
public PlaybackSession createPlaybackSession() {
try {
- String id = mService.getSessionId(mUserId);
+ String id = mService.getPlaybackSessionId(mUserId);
PlaybackSession session = new PlaybackSession(id, this);
return session;
} catch (RemoteException e) {
@@ -103,6 +103,21 @@ public class MediaMetricsManager {
}
/**
+ * Creates a recording session.
+ * @hide
+ */
+ @NonNull
+ public RecordingSession createRecordingSession() {
+ try {
+ String id = mService.getRecordingSessionId(mUserId);
+ RecordingSession session = new RecordingSession(id, this);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Reports error event.
* @hide
*/
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index 029edeb93374..098885cc9bdd 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,9 @@ import java.util.Objects;
public final class NetworkEvent extends Event implements Parcelable {
/** Network type is not specified. Default type. */
public static final int NETWORK_TYPE_NONE = 0;
+ // TODO: replace NONE with UNKNOWN
+ /** @hide */
+ public static final int NETWORK_TYPE_UNKNOWN = 0;
/** Other network type */
public static final int NETWORK_TYPE_OTHER = 1;
/** Wi-Fi network */
@@ -49,6 +53,9 @@ public final class NetworkEvent extends Event implements Parcelable {
public static final int NETWORK_TYPE_5G_NSA = 7;
/** 5G SA network */
public static final int NETWORK_TYPE_5G_SA = 8;
+ /** Not network connected */
+ /** @hide */
+ public static final int NETWORK_TYPE_OFFLINE = 9;
private final int mNetworkType;
private final long mTimeSinceCreatedMillis;
@@ -56,6 +63,7 @@ public final class NetworkEvent extends Event implements Parcelable {
/** @hide */
@IntDef(prefix = "NETWORK_TYPE_", value = {
NETWORK_TYPE_NONE,
+ NETWORK_TYPE_UNKNOWN,
NETWORK_TYPE_OTHER,
NETWORK_TYPE_WIFI,
NETWORK_TYPE_ETHERNET,
@@ -63,7 +71,8 @@ public final class NetworkEvent extends Event implements Parcelable {
NETWORK_TYPE_3G,
NETWORK_TYPE_4G,
NETWORK_TYPE_5G_NSA,
- NETWORK_TYPE_5G_SA
+ NETWORK_TYPE_5G_SA,
+ NETWORK_TYPE_OFFLINE
})
@Retention(RetentionPolicy.SOURCE)
public @interface NetworkType {}
@@ -92,6 +101,8 @@ public final class NetworkEvent extends Event implements Parcelable {
return "NETWORK_TYPE_5G_NSA";
case NETWORK_TYPE_5G_SA:
return "NETWORK_TYPE_5G_SA";
+ case NETWORK_TYPE_OFFLINE:
+ return "NETWORK_TYPE_OFFLINE";
default:
return Integer.toHexString(value);
}
@@ -102,9 +113,10 @@ public final class NetworkEvent extends Event implements Parcelable {
*
* @hide
*/
- public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis) {
+ public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis, Bundle extras) {
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mExtras = extras.deepCopy();
}
/**
@@ -149,8 +161,12 @@ public final class NetworkEvent extends Event implements Parcelable {
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ if (mExtras != null) flg |= 0x1;
+ dest.writeByte(flg);
dest.writeInt(mNetworkType);
dest.writeLong(mTimeSinceCreatedMillis);
+ if (mExtras != null) dest.writeBundle(mExtras);
}
@Override
@@ -160,11 +176,14 @@ public final class NetworkEvent extends Event implements Parcelable {
/** @hide */
/* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
int type = in.readInt();
long timeSinceCreatedMillis = in.readLong();
+ Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mExtras = extras;
}
/**
@@ -189,6 +208,7 @@ public final class NetworkEvent extends Event implements Parcelable {
public static final class Builder {
private int mNetworkType = NETWORK_TYPE_NONE;
private long mTimeSinceCreatedMillis = -1;
+ private Bundle mExtras;
/**
* Creates a new Builder.
@@ -214,9 +234,19 @@ public final class NetworkEvent extends Event implements Parcelable {
return this;
}
+ /**
+ * Set extras for compatibility.
+ * <p>Should be used by support library only.
+ * @hide
+ */
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
/** Builds the instance. */
public @NonNull NetworkEvent build() {
- NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis);
+ NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis, mExtras);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index 5a0820d16cb9..b23b4d2728b4 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -21,6 +21,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -63,11 +64,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
@Nullable String exceptionStack,
int errorCode,
int subErrorCode,
- long timeSinceCreatedMillis) {
+ long timeSinceCreatedMillis,
+ Bundle extras) {
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mExtras = extras.deepCopy();
}
/** @hide */
@@ -135,11 +138,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
byte flg = 0;
if (mExceptionStack != null) flg |= 0x1;
+ if (mExtras != null) flg |= 0x2;
dest.writeByte(flg);
if (mExceptionStack != null) dest.writeString(mExceptionStack);
dest.writeInt(mErrorCode);
dest.writeInt(mSubErrorCode);
dest.writeLong(mTimeSinceCreatedMillis);
+ if (mExtras != null) dest.writeBundle(mExtras);
}
@Override
@@ -154,11 +159,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
int errorCode = in.readInt();
int subErrorCode = in.readInt();
long timeSinceCreatedMillis = in.readLong();
+ Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mExtras = extras;
}
@@ -183,6 +190,7 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
private int mErrorCode;
private int mSubErrorCode;
private long mTimeSinceCreatedMillis = -1;
+ private Bundle mExtras;
/**
* Creates a new Builder.
@@ -226,6 +234,16 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
return this;
}
+ /**
+ * Set extras for compatibility.
+ * <p>Should be used by support library only.
+ * @hide
+ */
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
/** Builds the instance. */
public @NonNull PlaybackErrorEvent build() {
@@ -241,7 +259,8 @@ public final class PlaybackErrorEvent extends Event implements Parcelable {
stack,
mErrorCode,
mSubErrorCode,
- mTimeSinceCreatedMillis);
+ mTimeSinceCreatedMillis,
+ mExtras);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 4aa61662ba52..7e7f44a97b9c 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,6 +59,10 @@ public final class PlaybackMetrics implements Parcelable {
/** SS (HTTP Smooth Streaming) stream type. */
public static final int STREAM_TYPE_SS = 5;
+ /** Unknown playback type. */
+ // TODO: change the PLAYBACK_TYPE_ values
+ /** @hide */
+ public static final int PLAYBACK_TYPE_UNKNOWN = 0;
/** VOD (Video on Demand) playback type. */
public static final int PLAYBACK_TYPE_VOD = 0;
/** Live playback type. */
@@ -80,6 +85,10 @@ public final class PlaybackMetrics implements Parcelable {
/** Clear key DRM type. */
public static final int DRM_TYPE_CLEARKEY = 6;
+ /** Unknown content type. */
+ // TODO: change the CONTENT_TYPE_ values
+ /** @hide */
+ public static final int CONTENT_TYPE_UNKNOWN = 0;
/** Main contents. */
public static final int CONTENT_TYPE_MAIN = 0;
/** Advertisement contents. */
@@ -112,6 +121,7 @@ public final class PlaybackMetrics implements Parcelable {
/** @hide */
@IntDef(prefix = "PLAYBACK_TYPE_", value = {
+ PLAYBACK_TYPE_UNKNOWN,
PLAYBACK_TYPE_VOD,
PLAYBACK_TYPE_LIVE,
PLAYBACK_TYPE_OTHER
@@ -134,6 +144,7 @@ public final class PlaybackMetrics implements Parcelable {
/** @hide */
@IntDef(prefix = "CONTENT_TYPE_", value = {
+ CONTENT_TYPE_UNKNOWN,
CONTENT_TYPE_MAIN,
CONTENT_TYPE_AD,
CONTENT_TYPE_OTHER
@@ -158,6 +169,8 @@ public final class PlaybackMetrics implements Parcelable {
private final long mNetworkBytesRead;
private final long mLocalBytesRead;
private final long mNetworkTransferDurationMillis;
+ private final byte[] mDrmSessionId;
+ private final Bundle mExtras;
/**
* Creates a new PlaybackMetrics.
@@ -179,7 +192,9 @@ public final class PlaybackMetrics implements Parcelable {
int audioUnderrunCount,
long networkBytesRead,
long localBytesRead,
- long networkTransferDurationMillis) {
+ long networkTransferDurationMillis,
+ byte[] drmSessionId,
+ Bundle extras) {
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
this.mStreamType = streamType;
@@ -196,6 +211,8 @@ public final class PlaybackMetrics implements Parcelable {
this.mNetworkBytesRead = networkBytesRead;
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
+ this.mDrmSessionId = drmSessionId;
+ this.mExtras = extras.deepCopy();
}
/**
@@ -321,6 +338,12 @@ public final class PlaybackMetrics implements Parcelable {
return mNetworkTransferDurationMillis;
}
+ /** @hide */
+ @NonNull
+ public byte[] getDrmSessionId() {
+ return mDrmSessionId;
+ }
+
@Override
public String toString() {
return "PlaybackMetrics { "
@@ -339,6 +362,7 @@ public final class PlaybackMetrics implements Parcelable {
+ "networkBytesRead = " + mNetworkBytesRead + ", "
+ "localBytesRead = " + mLocalBytesRead + ", "
+ "networkTransferDurationMillis = " + mNetworkTransferDurationMillis
+ + "drmSessionId = " + Arrays.toString(mDrmSessionId)
+ " }";
}
@@ -361,7 +385,8 @@ public final class PlaybackMetrics implements Parcelable {
&& mAudioUnderrunCount == that.mAudioUnderrunCount
&& mNetworkBytesRead == that.mNetworkBytesRead
&& mLocalBytesRead == that.mLocalBytesRead
- && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis;
+ && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis
+ && Arrays.equals(mDrmSessionId, that.mDrmSessionId);
}
@Override
@@ -369,7 +394,7 @@ public final class PlaybackMetrics implements Parcelable {
return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType,
mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds,
mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead,
- mLocalBytesRead, mNetworkTransferDurationMillis);
+ mLocalBytesRead, mNetworkTransferDurationMillis, mDrmSessionId);
}
@Override
@@ -377,6 +402,7 @@ public final class PlaybackMetrics implements Parcelable {
long flg = 0;
if (mPlayerName != null) flg |= 0x80;
if (mPlayerVersion != null) flg |= 0x100;
+ if (mExtras != null) flg |= 0x200;
dest.writeLong(flg);
dest.writeLong(mMediaDurationMillis);
dest.writeInt(mStreamSource);
@@ -386,6 +412,7 @@ public final class PlaybackMetrics implements Parcelable {
dest.writeInt(mContentType);
if (mPlayerName != null) dest.writeString(mPlayerName);
if (mPlayerVersion != null) dest.writeString(mPlayerVersion);
+ if (mExtras != null) dest.writeBundle(mExtras);
dest.writeLongArray(mExperimentIds);
dest.writeInt(mVideoFramesPlayed);
dest.writeInt(mVideoFramesDropped);
@@ -393,6 +420,8 @@ public final class PlaybackMetrics implements Parcelable {
dest.writeLong(mNetworkBytesRead);
dest.writeLong(mLocalBytesRead);
dest.writeLong(mNetworkTransferDurationMillis);
+ dest.writeInt(mDrmSessionId.length);
+ dest.writeByteArray(mDrmSessionId);
}
@Override
@@ -411,6 +440,7 @@ public final class PlaybackMetrics implements Parcelable {
int contentType = in.readInt();
String playerName = (flg & 0x80) == 0 ? null : in.readString();
String playerVersion = (flg & 0x100) == 0 ? null : in.readString();
+ Bundle extras = (flg & 0x200) == 0 ? null : in.readBundle();
long[] experimentIds = in.createLongArray();
int videoFramesPlayed = in.readInt();
int videoFramesDropped = in.readInt();
@@ -418,6 +448,9 @@ public final class PlaybackMetrics implements Parcelable {
long networkBytesRead = in.readLong();
long localBytesRead = in.readLong();
long networkTransferDurationMillis = in.readLong();
+ int drmSessionIdLen = in.readInt();
+ byte[] drmSessionId = new byte[drmSessionIdLen];
+ in.readByteArray(drmSessionId);
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
@@ -435,6 +468,8 @@ public final class PlaybackMetrics implements Parcelable {
this.mNetworkBytesRead = networkBytesRead;
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
+ this.mDrmSessionId = drmSessionId;
+ this.mExtras = extras;
}
public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR =
@@ -470,6 +505,8 @@ public final class PlaybackMetrics implements Parcelable {
private long mNetworkBytesRead = -1;
private long mLocalBytesRead = -1;
private long mNetworkTransferDurationMillis = -1;
+ private byte[] mDrmSessionId = new byte[0];
+ private Bundle mExtras;
/**
* Creates a new Builder.
@@ -608,6 +645,24 @@ public final class PlaybackMetrics implements Parcelable {
return this;
}
+ /**
+ * @hide
+ */
+ public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) {
+ mDrmSessionId = drmSessionId;
+ return this;
+ }
+
+ /**
+ * Set extras for compatibility.
+ * <p>Should be used by support library only.
+ * @hide
+ */
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull PlaybackMetrics build() {
@@ -626,7 +681,9 @@ public final class PlaybackMetrics implements Parcelable {
mAudioUnderrunCount,
mNetworkBytesRead,
mLocalBytesRead,
- mNetworkTransferDurationMillis);
+ mNetworkTransferDurationMillis,
+ mDrmSessionId,
+ mExtras);
return o;
}
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 4ee8a45fe196..272fd9bd5576 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -29,6 +29,7 @@ import java.util.Objects;
public final class PlaybackSession implements AutoCloseable {
private final @NonNull String mId;
private final @NonNull MediaMetricsManager mManager;
+ private final @NonNull LogSessionId mLogSessionId;
private boolean mClosed = false;
/**
@@ -41,6 +42,7 @@ public final class PlaybackSession implements AutoCloseable {
mManager = manager;
AnnotationValidations.validate(NonNull.class, null, mId);
AnnotationValidations.validate(NonNull.class, null, mManager);
+ mLogSessionId = new LogSessionId(mId);
}
/**
@@ -79,9 +81,16 @@ public final class PlaybackSession implements AutoCloseable {
}
public @NonNull String getId() {
+ // TODO: remove this method and use getSessionId();
return mId;
}
+ /** @hide */
+ public @NonNull LogSessionId getSessionId() {
+ // TODO: remove getId() and use this method;
+ return mLogSessionId;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index 8ca5b75dec98..dea8c1db71de 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -136,9 +137,11 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
*/
public PlaybackStateEvent(
int state,
- long timeSinceCreatedMillis) {
+ long timeSinceCreatedMillis,
+ Bundle extras) {
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mState = state;
+ this.mExtras = extras.deepCopy();
}
/**
@@ -174,8 +177,12 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ byte flg = 0;
+ if (mExtras != null) flg |= 0x1;
+ dest.writeByte(flg);
dest.writeInt(mState);
dest.writeLong(mTimeSinceCreatedMillis);
+ if (mExtras != null) dest.writeBundle(mExtras);
}
@Override
@@ -185,11 +192,14 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
/** @hide */
/* package-private */ PlaybackStateEvent(@NonNull Parcel in) {
+ byte flg = in.readByte();
int state = in.readInt();
long timeSinceCreatedMillis = in.readLong();
+ Bundle extras = (flg & 0x1) == 0 ? null : in.readBundle();
this.mState = state;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mExtras = extras;
}
public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR =
@@ -211,6 +221,7 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
public static final class Builder {
private int mState = STATE_NOT_STARTED;
private long mTimeSinceCreatedMillis = -1;
+ private Bundle mExtras;
/**
* Creates a new Builder.
@@ -236,11 +247,22 @@ public final class PlaybackStateEvent extends Event implements Parcelable {
return this;
}
+ /**
+ * Set extras for compatibility.
+ * <p>Should be used by support library only.
+ * @hide
+ */
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
/** Builds the instance. */
public @NonNull PlaybackStateEvent build() {
PlaybackStateEvent o = new PlaybackStateEvent(
mState,
- mTimeSinceCreatedMillis);
+ mTimeSinceCreatedMillis,
+ mExtras);
return o;
}
}
diff --git a/media/java/android/media/metrics/RecordingSession.java b/media/java/android/media/metrics/RecordingSession.java
new file mode 100644
index 000000000000..541d129604ad
--- /dev/null
+++ b/media/java/android/media/metrics/RecordingSession.java
@@ -0,0 +1,62 @@
+/*
+ * 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.media.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.Objects;
+
+/**
+ * An instances of this class represents a session of media recording.
+ * @hide
+ */
+public final class RecordingSession implements AutoCloseable {
+ private final @NonNull String mId;
+ private final @NonNull MediaMetricsManager mManager;
+ private final @NonNull LogSessionId mLogSessionId;
+ private boolean mClosed = false;
+
+ /** @hide */
+ public RecordingSession(@NonNull String id, @NonNull MediaMetricsManager manager) {
+ mId = id;
+ mManager = manager;
+ AnnotationValidations.validate(NonNull.class, null, mId);
+ AnnotationValidations.validate(NonNull.class, null, mManager);
+ mLogSessionId = new LogSessionId(mId);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RecordingSession that = (RecordingSession) o;
+ return Objects.equals(mId, that.mId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId);
+ }
+
+ @Override
+ public void close() {
+ mClosed = true;
+ }
+}
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index ef25357457c5..aa519782ec80 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -16,10 +16,12 @@
package android.media.metrics;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -65,9 +67,10 @@ public final class TrackChangeEvent extends Event implements Parcelable {
private final @Nullable String mLanguage;
private final @Nullable String mLanguageRegion;
private final int mChannelCount;
- private final int mSampleRate;
+ private final int mAudioSampleRate;
private final int mWidth;
private final int mHeight;
+ private final float mVideoFrameRate;
@@ -99,6 +102,7 @@ public final class TrackChangeEvent extends Event implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface TrackType {}
+ // TODO: remove this constructor. Use the private one below.
public TrackChangeEvent(
int state,
int reason,
@@ -125,9 +129,45 @@ public final class TrackChangeEvent extends Event implements Parcelable {
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
this.mChannelCount = channelCount;
- this.mSampleRate = sampleRate;
+ this.mAudioSampleRate = sampleRate;
this.mWidth = width;
this.mHeight = height;
+ this.mVideoFrameRate = -1;
+ }
+
+ private TrackChangeEvent(
+ int state,
+ int reason,
+ @Nullable String containerMimeType,
+ @Nullable String sampleMimeType,
+ @Nullable String codecName,
+ int bitrate,
+ long timeSinceCreatedMillis,
+ int type,
+ @Nullable String language,
+ @Nullable String languageRegion,
+ int channelCount,
+ int sampleRate,
+ int width,
+ int height,
+ float videoFrameRate,
+ @Nullable Bundle extras) {
+ this.mState = state;
+ this.mReason = reason;
+ this.mContainerMimeType = containerMimeType;
+ this.mSampleMimeType = sampleMimeType;
+ this.mCodecName = codecName;
+ this.mBitrate = bitrate;
+ this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ this.mType = type;
+ this.mLanguage = language;
+ this.mLanguageRegion = languageRegion;
+ this.mChannelCount = channelCount;
+ this.mAudioSampleRate = sampleRate;
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mVideoFrameRate = videoFrameRate;
+ this.mExtras = extras.deepCopy();
}
/**
@@ -223,7 +263,7 @@ public final class TrackChangeEvent extends Event implements Parcelable {
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getSampleRate() {
- return mSampleRate;
+ return mAudioSampleRate;
}
/**
@@ -244,6 +284,16 @@ public final class TrackChangeEvent extends Event implements Parcelable {
return mHeight;
}
+ /**
+ * Gets video frame rate.
+ * @return the video frame rate, or -1 if unknown.
+ * @hide
+ */
+ @FloatRange(from = -1, to = Float.MAX_VALUE)
+ public float getVideoFrameRate() {
+ return mVideoFrameRate;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
int flg = 0;
@@ -252,6 +302,7 @@ public final class TrackChangeEvent extends Event implements Parcelable {
if (mCodecName != null) flg |= 0x10;
if (mLanguage != null) flg |= 0x100;
if (mLanguageRegion != null) flg |= 0x200;
+ if (mExtras != null) flg |= 0x400;
dest.writeInt(flg);
dest.writeInt(mState);
dest.writeInt(mReason);
@@ -264,9 +315,11 @@ public final class TrackChangeEvent extends Event implements Parcelable {
if (mLanguage != null) dest.writeString(mLanguage);
if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
dest.writeInt(mChannelCount);
- dest.writeInt(mSampleRate);
+ dest.writeInt(mAudioSampleRate);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
+ dest.writeFloat(mVideoFrameRate);
+ if (mExtras != null) dest.writeBundle(mExtras);
}
@Override
@@ -291,6 +344,8 @@ public final class TrackChangeEvent extends Event implements Parcelable {
int sampleRate = in.readInt();
int width = in.readInt();
int height = in.readInt();
+ float videoFrameRate = in.readFloat();
+ Bundle extras = (flg & 0x400) == 0 ? null : in.readBundle();
this.mState = state;
this.mReason = reason;
@@ -303,9 +358,11 @@ public final class TrackChangeEvent extends Event implements Parcelable {
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
this.mChannelCount = channelCount;
- this.mSampleRate = sampleRate;
+ this.mAudioSampleRate = sampleRate;
this.mWidth = width;
this.mHeight = height;
+ this.mVideoFrameRate = videoFrameRate;
+ this.mExtras = extras;
}
public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
@@ -335,9 +392,10 @@ public final class TrackChangeEvent extends Event implements Parcelable {
+ "language = " + mLanguage + ", "
+ "languageRegion = " + mLanguageRegion + ", "
+ "channelCount = " + mChannelCount + ", "
- + "sampleRate = " + mSampleRate + ", "
+ + "sampleRate = " + mAudioSampleRate + ", "
+ "width = " + mWidth + ", "
- + "height = " + mHeight
+ + "height = " + mHeight + ", "
+ + "videoFrameRate = " + mVideoFrameRate
+ " }";
}
@@ -357,16 +415,17 @@ public final class TrackChangeEvent extends Event implements Parcelable {
&& Objects.equals(mLanguage, that.mLanguage)
&& Objects.equals(mLanguageRegion, that.mLanguageRegion)
&& mChannelCount == that.mChannelCount
- && mSampleRate == that.mSampleRate
+ && mAudioSampleRate == that.mAudioSampleRate
&& mWidth == that.mWidth
- && mHeight == that.mHeight;
+ && mHeight == that.mHeight
+ && mVideoFrameRate == that.mVideoFrameRate;
}
@Override
public int hashCode() {
return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion,
- mChannelCount, mSampleRate, mWidth, mHeight);
+ mChannelCount, mAudioSampleRate, mWidth, mHeight, mVideoFrameRate);
}
/**
@@ -385,9 +444,11 @@ public final class TrackChangeEvent extends Event implements Parcelable {
private @Nullable String mLanguage;
private @Nullable String mLanguageRegion;
private int mChannelCount = -1;
- private int mSampleRate = -1;
+ private int mAudioSampleRate = -1;
private int mWidth = -1;
private int mHeight = -1;
+ private float mVideoFrameRate = -1;
+ private Bundle mExtras;
private long mBuilderFieldsSet = 0L;
@@ -512,9 +573,10 @@ public final class TrackChangeEvent extends Event implements Parcelable {
*/
public @NonNull Builder setSampleRate(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
+ // TODO: rename it to setAudioSampleRate
checkNotUsed();
mBuilderFieldsSet |= 0x800;
- mSampleRate = value;
+ mAudioSampleRate = value;
return this;
}
@@ -540,6 +602,28 @@ public final class TrackChangeEvent extends Event implements Parcelable {
return this;
}
+ /**
+ * Sets video frame rate.
+ * @param value the video frame rate. -1 indicates the value is unknown.
+ * @hide
+ */
+ public @NonNull Builder setVideoFrameRate(
+ @FloatRange(from = -1, to = Float.MAX_VALUE) float value) {
+ checkNotUsed();
+ mVideoFrameRate = value;
+ return this;
+ }
+
+ /**
+ * Set extras for compatibility.
+ * <p>Should be used by support library only.
+ * @hide
+ */
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull TrackChangeEvent build() {
checkNotUsed();
@@ -557,9 +641,11 @@ public final class TrackChangeEvent extends Event implements Parcelable {
mLanguage,
mLanguageRegion,
mChannelCount,
- mSampleRate,
+ mAudioSampleRate,
mWidth,
- mHeight);
+ mHeight,
+ mVideoFrameRate,
+ mExtras);
return o;
}
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 307d80dc15c0..07866ac34e4c 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -105,6 +105,7 @@ static const JavaMediaCodecListWrapper *getCodecList(JNIEnv *env) {
// This should never happen unless something is really wrong
jniThrowException(
env, "java/lang/RuntimeException", "cannot get MediaCodecList");
+ return NULL;
}
sListWrapper.reset(new JavaMediaCodecListWrapper(mcl));
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/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index e620dfbafa08..8b448877c15b 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -107,12 +107,10 @@ public class CompanionDeviceDiscoveryService extends Service {
String callingPackage,
IFindDeviceCallback findCallback,
AndroidFuture serviceCallback) {
- if (DEBUG) {
- Log.i(LOG_TAG,
- "startDiscovery() called with: filter = [" + request
- + "], findCallback = [" + findCallback + "]"
- + "], serviceCallback = [" + serviceCallback + "]");
- }
+ Log.i(LOG_TAG,
+ "startDiscovery() called with: filter = [" + request
+ + "], findCallback = [" + findCallback + "]"
+ + "], serviceCallback = [" + serviceCallback + "]");
mFindCallback = findCallback;
mServiceCallback = serviceCallback;
Handler.getMain().sendMessage(obtainMessage(
@@ -127,7 +125,7 @@ public class CompanionDeviceDiscoveryService extends Service {
@Override
public IBinder onBind(Intent intent) {
- if (DEBUG) Log.i(LOG_TAG, "onBind(" + intent + ")");
+ Log.i(LOG_TAG, "onBind(" + intent + ")");
return mBinder.asBinder();
}
@@ -135,7 +133,7 @@ public class CompanionDeviceDiscoveryService extends Service {
public void onCreate() {
super.onCreate();
- if (DEBUG) Log.i(LOG_TAG, "onCreate()");
+ Log.i(LOG_TAG, "onCreate()");
mBluetoothManager = getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
@@ -160,7 +158,9 @@ public class CompanionDeviceDiscoveryService extends Service {
= CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);
reset();
- } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
+ } else {
+ Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
+ }
if (!ArrayUtils.isEmpty(mDevicesFound)) {
onReadyToShowUI();
@@ -197,17 +197,20 @@ public class CompanionDeviceDiscoveryService extends Service {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
+ Log.i(LOG_TAG, "registerReceiver(BluetoothDevice.ACTION_FOUND)");
mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver();
registerReceiver(mBluetoothBroadcastReceiver, intentFilter);
mBluetoothAdapter.startDiscovery();
}
if (shouldScan(mBLEFilters) && mBLEScanner != null) {
+ Log.i(LOG_TAG, "BLEScanner.startScan");
mBLEScanCallback = new BLEScanCallback();
mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback);
}
if (shouldScan(mWifiFilters)) {
+ Log.i(LOG_TAG, "registerReceiver(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)");
mWifiBroadcastReceiver = new WifiBroadcastReceiver();
registerReceiver(mWifiBroadcastReceiver,
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
@@ -225,7 +228,7 @@ public class CompanionDeviceDiscoveryService extends Service {
@MainThread
private void reset() {
- if (DEBUG) Log.i(LOG_TAG, "reset()");
+ Log.i(LOG_TAG, "reset()");
stopScan();
mDevicesFound.clear();
mSelectedDevice = null;
@@ -234,12 +237,13 @@ public class CompanionDeviceDiscoveryService extends Service {
@Override
public boolean onUnbind(Intent intent) {
+ Log.i(LOG_TAG, "onUnbind(intent = " + intent + ")");
stopScan();
return super.onUnbind(intent);
}
private void stopScan() {
- if (DEBUG) Log.i(LOG_TAG, "stopScan()");
+ Log.i(LOG_TAG, "stopScan()");
if (!mIsScanning) return;
mIsScanning = false;
diff --git a/core/java/android/net/QosFilterParcelable.aidl b/packages/Connectivity/framework/aidl-export/android/net/QosFilterParcelable.aidl
index 312d6352ee92..312d6352ee92 100644
--- a/core/java/android/net/QosFilterParcelable.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/QosFilterParcelable.aidl
diff --git a/core/java/android/net/QosSession.aidl b/packages/Connectivity/framework/aidl-export/android/net/QosSession.aidl
index c2cf36624b55..c2cf36624b55 100644
--- a/core/java/android/net/QosSession.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/QosSession.aidl
diff --git a/core/java/android/net/QosSocketInfo.aidl b/packages/Connectivity/framework/aidl-export/android/net/QosSocketInfo.aidl
index 476c0900e23e..476c0900e23e 100644
--- a/core/java/android/net/QosSocketInfo.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/QosSocketInfo.aidl
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 31b8fc8ae53a..a8f1a4d2a7f8 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -401,16 +401,6 @@ package android.net {
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
- public final class Proxy {
- ctor public Proxy();
- method @Deprecated public static String getDefaultHost();
- method @Deprecated public static int getDefaultPort();
- method @Deprecated public static String getHost(android.content.Context);
- method @Deprecated public static int getPort(android.content.Context);
- field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
- field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
- }
-
public class ProxyInfo implements android.os.Parcelable {
ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
method public static android.net.ProxyInfo buildDirectProxy(String, int);
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 3af855ec1e11..d2ed73ef8298 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -6,6 +6,7 @@ package android.net {
}
public class ConnectivityManager {
+ method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
@@ -23,10 +24,6 @@ package android.net {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
- public final class Proxy {
- method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
- }
-
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 41ebc5774f3d..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
@@ -308,6 +308,9 @@ package android.net {
field public static final int ID_NONE = -1; // 0xffffffff
}
+ public class NetworkReleasedException extends java.lang.Exception {
+ }
+
public class NetworkRequest implements android.os.Parcelable {
method @Nullable public String getRequestorPackageName();
method public int getRequestorUid();
@@ -317,6 +320,47 @@ package android.net {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
}
+ public abstract class QosCallback {
+ ctor public QosCallback();
+ method public void onError(@NonNull android.net.QosCallbackException);
+ method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes);
+ method public void onQosSessionLost(@NonNull android.net.QosSession);
+ }
+
+ public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException {
+ }
+
+ public final class QosCallbackException extends java.lang.Exception {
+ }
+
+ public abstract class QosFilter {
+ method @NonNull public abstract android.net.Network getNetwork();
+ method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
+ }
+
+ public final class QosSession implements android.os.Parcelable {
+ ctor public QosSession(int, int);
+ method public int describeContents();
+ method public int getSessionId();
+ method public int getSessionType();
+ method public long getUniqueId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
+ field public static final int TYPE_EPS_BEARER = 1; // 0x1
+ }
+
+ public interface QosSessionAttributes {
+ }
+
+ public final class QosSocketInfo implements android.os.Parcelable {
+ ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
+ method public int describeContents();
+ method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
+ method @NonNull public android.net.Network getNetwork();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
+ }
+
public final class RouteInfo implements android.os.Parcelable {
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
@@ -331,6 +375,12 @@ package android.net {
field public static final int SUCCESS = 0; // 0x0
}
+ public class SocketLocalAddressChangedException extends java.lang.Exception {
+ }
+
+ public class SocketNotBoundException extends java.lang.Exception {
+ }
+
public final class StaticIpConfiguration implements android.os.Parcelable {
ctor public StaticIpConfiguration();
ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -392,16 +442,3 @@ package android.net.apf {
}
-package android.net.util {
-
- public final class SocketUtils {
- method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
- method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
- method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
- method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
- method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
- method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
- }
-
-}
-
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/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index e7ab0a1c7ac8..d7c6854496b7 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1259,6 +1259,25 @@ public class ConnectivityManager {
}
/**
+ * Return a list of {@link NetworkStateSnapshot}s, one for each network that is currently
+ * connected.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ @NonNull
+ public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+ try {
+ return mService.getAllNetworkStateSnapshot();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the {@link Network} object currently serving a given type, or
* null if the given type is not connected.
*
@@ -2245,31 +2264,6 @@ public class ConnectivityManager {
}
}
- /* TODO: These permissions checks don't belong in client-side code. Move them to
- * services.jar, possibly in com.android.server.net. */
-
- /** {@hide} */
- public static final void enforceChangePermission(Context context,
- String callingPkg, String callingAttributionTag) {
- int uid = Binder.getCallingUid();
- checkAndNoteChangeNetworkStateOperation(context, uid, callingPkg,
- callingAttributionTag, true /* throwException */);
- }
-
- /**
- * Check if the package is a allowed to change the network state. This also accounts that such
- * an access happened.
- *
- * @return {@code true} iff the package is allowed to change the network state.
- */
- // TODO: Remove method and replace with direct call once R code is pushed to AOSP
- private static boolean checkAndNoteChangeNetworkStateOperation(@NonNull Context context,
- int uid, @NonNull String callingPackage, @Nullable String callingAttributionTag,
- boolean throwException) {
- return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, callingPackage,
- callingAttributionTag, throwException);
- }
-
/**
* Check if the package is a allowed to write settings. This also accounts that such an access
* happened.
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/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 160338d396af..cd49258d1c47 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.UidRange;
@@ -79,6 +80,8 @@ interface IConnectivityManager
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
NetworkState[] getAllNetworkState();
+ List<NetworkStateSnapshot> getAllNetworkStateSnapshot();
+
boolean isActiveNetworkMetered();
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
diff --git a/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl b/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
index 7979afc54f90..7979afc54f90 100644
--- a/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl
+++ b/packages/Connectivity/framework/src/android/net/IOnSetOemNetworkPreferenceListener.aidl
diff --git a/core/java/android/net/IQosCallback.aidl b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
index 91c75759f85c..91c75759f85c 100644
--- a/core/java/android/net/IQosCallback.aidl
+++ b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl
diff --git a/core/java/android/net/NetworkReleasedException.java b/packages/Connectivity/framework/src/android/net/NetworkReleasedException.java
index 0629b7563aea..0629b7563aea 100644
--- a/core/java/android/net/NetworkReleasedException.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkReleasedException.java
diff --git a/core/java/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
index 813fde1c15f2..d01026566ca0 100644
--- a/core/java/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -115,7 +115,8 @@ public class NetworkState implements Parcelable {
}
@UnsupportedAppUsage
- public static final @android.annotation.NonNull Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+ @NonNull
+ public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
@Override
public NetworkState createFromParcel(Parcel in) {
return new NetworkState(in);
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index b5e8a614b8ea..9e42bbecbe9d 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -87,22 +87,6 @@ public class NetworkUtils {
public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
- * Protect {@code fd} from VPN connections. After protecting, data sent through
- * this socket will go directly to the underlying network, so its traffic will not be
- * forwarded through the VPN.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553,
- publicAlternatives = "Use {@link android.net.VpnService#protect} instead.")
- public static native boolean protectFromVpn(FileDescriptor fd);
-
- /**
- * Protect {@code socketfd} from VPN connections. After protecting, data sent through
- * this socket will go directly to the underlying network, so its traffic will not be
- * forwarded through the VPN.
- */
- public native static boolean protectFromVpn(int socketfd);
-
- /**
* Determine if {@code uid} can access network designated by {@code netId}.
* @return {@code true} if {@code uid} can access network, {@code false} otherwise.
*/
diff --git a/core/java/android/net/QosCallback.java b/packages/Connectivity/framework/src/android/net/QosCallback.java
index 22f06bc0e690..22f06bc0e690 100644
--- a/core/java/android/net/QosCallback.java
+++ b/packages/Connectivity/framework/src/android/net/QosCallback.java
diff --git a/core/java/android/net/QosCallbackConnection.java b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
index bdb4ad68cd7b..bdb4ad68cd7b 100644
--- a/core/java/android/net/QosCallbackConnection.java
+++ b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java
diff --git a/core/java/android/net/QosCallbackException.java b/packages/Connectivity/framework/src/android/net/QosCallbackException.java
index 7fd9a527e2ac..7fd9a527e2ac 100644
--- a/core/java/android/net/QosCallbackException.java
+++ b/packages/Connectivity/framework/src/android/net/QosCallbackException.java
diff --git a/core/java/android/net/QosFilter.java b/packages/Connectivity/framework/src/android/net/QosFilter.java
index ab55002e02b3..ab55002e02b3 100644
--- a/core/java/android/net/QosFilter.java
+++ b/packages/Connectivity/framework/src/android/net/QosFilter.java
diff --git a/core/java/android/net/QosFilterParcelable.java b/packages/Connectivity/framework/src/android/net/QosFilterParcelable.java
index da3b2cf8ff7a..da3b2cf8ff7a 100644
--- a/core/java/android/net/QosFilterParcelable.java
+++ b/packages/Connectivity/framework/src/android/net/QosFilterParcelable.java
diff --git a/core/java/android/net/QosSession.java b/packages/Connectivity/framework/src/android/net/QosSession.java
index 4f3bb77c5877..4f3bb77c5877 100644
--- a/core/java/android/net/QosSession.java
+++ b/packages/Connectivity/framework/src/android/net/QosSession.java
diff --git a/core/java/android/net/QosSessionAttributes.java b/packages/Connectivity/framework/src/android/net/QosSessionAttributes.java
index 7a885942d1b5..7a885942d1b5 100644
--- a/core/java/android/net/QosSessionAttributes.java
+++ b/packages/Connectivity/framework/src/android/net/QosSessionAttributes.java
diff --git a/core/java/android/net/QosSocketFilter.java b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java
index 2080e68f5fba..2080e68f5fba 100644
--- a/core/java/android/net/QosSocketFilter.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketFilter.java
diff --git a/core/java/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index d37c4691ddde..d37c4691ddde 100644
--- a/core/java/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
diff --git a/core/java/android/net/SocketLocalAddressChangedException.java b/packages/Connectivity/framework/src/android/net/SocketLocalAddressChangedException.java
index 9daad83fd13e..9daad83fd13e 100644
--- a/core/java/android/net/SocketLocalAddressChangedException.java
+++ b/packages/Connectivity/framework/src/android/net/SocketLocalAddressChangedException.java
diff --git a/core/java/android/net/SocketNotBoundException.java b/packages/Connectivity/framework/src/android/net/SocketNotBoundException.java
index b1d7026ac981..b1d7026ac981 100644
--- a/core/java/android/net/SocketNotBoundException.java
+++ b/packages/Connectivity/framework/src/android/net/SocketNotBoundException.java
diff --git a/core/java/android/net/UidRange.aidl b/packages/Connectivity/framework/src/android/net/UidRange.aidl
index f70fc8e2fefd..f70fc8e2fefd 100644
--- a/core/java/android/net/UidRange.aidl
+++ b/packages/Connectivity/framework/src/android/net/UidRange.aidl
diff --git a/core/java/android/net/UidRange.java b/packages/Connectivity/framework/src/android/net/UidRange.java
index f0e7da78d669..26518d32edcb 100644
--- a/core/java/android/net/UidRange.java
+++ b/packages/Connectivity/framework/src/android/net/UidRange.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.os.UserHandle.PER_USER_RANGE;
-
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -52,14 +50,15 @@ public final class UidRange implements Parcelable {
/** Returns the smallest user Id which is contained in this UidRange */
public int getStartUser() {
- return start / PER_USER_RANGE;
+ return UserHandle.getUserHandleForUid(start).getIdentifier();
}
/** Returns the largest user Id which is contained in this UidRange */
public int getEndUser() {
- return stop / PER_USER_RANGE;
+ return UserHandle.getUserHandleForUid(stop).getIdentifier();
}
+ /** Returns whether the UidRange contains the specified UID. */
public boolean contains(int uid) {
return start <= uid && uid <= stop;
}
@@ -72,7 +71,7 @@ public final class UidRange implements Parcelable {
}
/**
- * @return {@code true} if this range contains every UID contained by the {@param other} range.
+ * @return {@code true} if this range contains every UID contained by the {@code other} range.
*/
public boolean containsRange(UidRange other) {
return start <= other.start && other.stop <= stop;
@@ -118,18 +117,18 @@ public final class UidRange implements Parcelable {
}
public static final @android.annotation.NonNull Creator<UidRange> CREATOR =
- new Creator<UidRange>() {
- @Override
- public UidRange createFromParcel(Parcel in) {
- int start = in.readInt();
- int stop = in.readInt();
+ new Creator<UidRange>() {
+ @Override
+ public UidRange createFromParcel(Parcel in) {
+ int start = in.readInt();
+ int stop = in.readInt();
- return new UidRange(start, stop);
- }
- @Override
- public UidRange[] newArray(int size) {
- return new UidRange[size];
- }
+ return new UidRange(start, stop);
+ }
+ @Override
+ public UidRange[] newArray(int size) {
+ return new UidRange[size];
+ }
};
/**
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 43fffd733e91..739ddada50b4 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -30,8 +30,8 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
-import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -92,8 +92,8 @@ public class MultinetworkPolicyTracker {
}
@VisibleForTesting
- protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener
- implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+ protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
+ implements TelephonyCallback.ActiveDataSubscriptionIdListener {
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
mActiveSubId = subId;
@@ -121,8 +121,8 @@ public class MultinetworkPolicyTracker {
}
};
- ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener(
- new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener());
+ ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback(
+ new HandlerExecutor(handler), new ActiveDataSubscriptionIdListener());
updateAvoidBadWifi();
updateMeteredMultipathPreference();
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index f20b89fb842c..e65b7b423bdc 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -63,6 +63,7 @@ java_library {
"unsupportedappusage",
],
static_libs: [
+ "modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",
"netd-client",
diff --git a/packages/Connectivity/service/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt
index ef53ebb43c40..d8205bf780fd 100644
--- a/packages/Connectivity/service/jarjar-rules.txt
+++ b/packages/Connectivity/service/jarjar-rules.txt
@@ -1 +1,2 @@
-rule com.android.net.module.util.** com.android.connectivity.util.@1 \ No newline at end of file
+rule com.android.net.module.util.** com.android.connectivity.net-utils.@1
+rule com.android.modules.utils.** com.android.connectivity.modules-utils.@1 \ No newline at end of file
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 7f19662c6961..c1dca5df1b2f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -394,16 +394,20 @@ public class DynamicSystemInstallationService extends Service
}
private void executeNotifyIfInUseCommand() {
- int status = getStatus();
-
- if (status == STATUS_IN_USE) {
- startForeground(NOTIFICATION_ID,
- buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
- } else if (status == STATUS_READY) {
- startForeground(NOTIFICATION_ID,
- buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
- } else {
- stopSelf();
+ switch (getStatus()) {
+ case STATUS_IN_USE:
+ startForeground(NOTIFICATION_ID,
+ buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+ break;
+ case STATUS_READY:
+ startForeground(NOTIFICATION_ID,
+ buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+ break;
+ case STATUS_IN_PROGRESS:
+ break;
+ case STATUS_NOT_STARTED:
+ default:
+ stopSelf();
}
}
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..efa9f3c16b48 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -64,8 +64,12 @@
<string name="wifi_security_eap" translatable="false">WPA/WPA2/WPA3-Enterprise</string>
<!-- Do not translate. Concise terminology for wifi with WPA 802.1x EAP security -->
<string name="wifi_security_eap_wpa" translatable="false">WPA-Enterprise</string>
+ <!-- Do not translate. Concise terminology for wifi with 802.1x EAP security -->
+ <string name="wifi_security_eap_wpa_wpa2" translatable="false">WPA/WPA2-Enterprise</string>
<!-- Do not translate. Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
<string name="wifi_security_eap_wpa2_wpa3" translatable="false">WPA2/WPA3-Enterprise</string>
+ <!-- Do not translate. Concise terminology for wifi with WPA3 802.1x EAP security -->
+ <string name="wifi_security_eap_wpa3" translatable="false">WPA3-Enterprise</string>
<!-- Do not translate. Concise terminology for Passpoint network -->
<string name="wifi_security_passpoint" translatable="false">Passpoint</string>
<!-- Do not translate. Terminology for wifi with WPA3 security -->
@@ -1059,7 +1063,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 +1306,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/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
index 43717aba3abd..dfde3c7a2512 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -27,7 +27,7 @@ import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.provider.Settings;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -72,7 +72,10 @@ public class ConnectivitySubsystemsRecoveryManager {
checkIfAllSubsystemsRestartsAreDone();
}
};
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ private final MobileTelephonyCallback mTelephonyCallback = new MobileTelephonyCallback();
+
+ private class MobileTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.RadioPowerStateListener {
@Override
public void onRadioPowerStateChanged(int state) {
if (!mTelephonyRestartInProgress || mCurrentRecoveryCallback == null) {
@@ -85,7 +88,7 @@ public class ConnectivitySubsystemsRecoveryManager {
checkIfAllSubsystemsRestartsAreDone();
}
}
- };
+ }
public ConnectivitySubsystemsRecoveryManager(@NonNull Context context,
@NonNull Handler handler) {
@@ -201,12 +204,12 @@ public class ConnectivitySubsystemsRecoveryManager {
}
private void startTrackingTelephonyRestart() {
- mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler),
- mPhoneStateListener);
+ mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+ mTelephonyCallback);
}
private void stopTrackingTelephonyRestart() {
- mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
}
private void checkIfAllSubsystemsRestartsAreDone() {
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/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 0cd5e4ded168..1a08366734bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -17,11 +17,11 @@ package com.android.settingslib.mobile;
import android.os.Handler;
import android.os.Looper;
-import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -40,9 +40,9 @@ public class MobileStatusTracker {
private final SubscriptionInfo mSubscriptionInfo;
private final Callback mCallback;
private final MobileStatus mMobileStatus;
- private final PhoneStateListener mPhoneStateListener;
private final SubscriptionDefaults mDefaults;
private final Handler mReceiverHandler;
+ private final MobileTelephonyCallback mTelephonyCallback;
/**
* MobileStatusTracker constructors
@@ -58,7 +58,7 @@ public class MobileStatusTracker {
SubscriptionInfo info, SubscriptionDefaults defaults, Callback callback) {
mPhone = phone;
mReceiverHandler = new Handler(receiverLooper);
- mPhoneStateListener = new MobilePhoneStateListener();
+ mTelephonyCallback = new MobileTelephonyCallback();
mSubscriptionInfo = info;
mDefaults = defaults;
mCallback = callback;
@@ -68,8 +68,8 @@ public class MobileStatusTracker {
/* updateTelephony= */false, new MobileStatus(mMobileStatus)));
}
- public PhoneStateListener getPhoneStateListener() {
- return mPhoneStateListener;
+ public MobileTelephonyCallback getTelephonyCallback() {
+ return mTelephonyCallback;
}
/**
@@ -77,9 +77,9 @@ public class MobileStatusTracker {
*/
public void setListening(boolean listening) {
if (listening) {
- mPhone.registerPhoneStateListener(mReceiverHandler::post, mPhoneStateListener);
+ mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
} else {
- mPhone.unregisterPhoneStateListener(mPhoneStateListener);
+ mPhone.unregisterTelephonyCallback(mTelephonyCallback);
}
}
@@ -106,15 +106,14 @@ public class MobileStatusTracker {
|| activity == TelephonyManager.DATA_ACTIVITY_OUT;
}
- private class MobilePhoneStateListener extends PhoneStateListener implements
- PhoneStateListener.ServiceStateChangedListener,
- PhoneStateListener.SignalStrengthsChangedListener,
- PhoneStateListener.CallStateChangedListener,
- PhoneStateListener.DataConnectionStateChangedListener,
- PhoneStateListener.DataActivityListener,
- PhoneStateListener.CarrierNetworkChangeListener,
- PhoneStateListener.ActiveDataSubscriptionIdChangedListener,
- PhoneStateListener.DisplayInfoChangedListener{
+ public class MobileTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.ServiceStateListener,
+ TelephonyCallback.SignalStrengthsListener,
+ TelephonyCallback.DataConnectionStateListener,
+ TelephonyCallback.DataActivityListener,
+ TelephonyCallback.CarrierNetworkListener,
+ TelephonyCallback.ActiveDataSubscriptionIdListener,
+ TelephonyCallback.DisplayInfoListener{
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index ecd40667843e..f9584a3e15e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -38,6 +38,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.MediaStore;
+import android.util.EventLog;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@@ -126,6 +127,14 @@ public class EditUserPhotoController {
}
final Uri pictureUri = data != null && data.getData() != null
? data.getData() : mTakePictureUri;
+
+ // Check if the result is a content uri
+ if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) {
+ Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme());
+ EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath());
+ return false;
+ }
+
switch (requestCode) {
case REQUEST_CODE_CROP_PHOTO:
onPhotoCropped(pictureUri);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index a81a05f1147a..303ee3c9fca2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -223,7 +223,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
public static final int SECURITY_OWE = 4;
public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
- public static final int SECURITY_MAX_VAL = 7; // Has to be the last
+ public static final int SECURITY_EAP_WPA3_ENTERPRISE = 7;
+ public static final int SECURITY_MAX_VAL = 8; // Has to be the last
private static final int PSK_UNKNOWN = 0;
private static final int PSK_WPA = 1;
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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 71e09106368b..1393116a814e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -164,6 +164,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
<uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
<uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
+ <uses-permission android:name="android.permission.MANAGE_UI_TRANSLATION" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
index 6f91d770012a..edfc0aba5be7 100644
--- a/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
+++ b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
@@ -20,6 +20,7 @@
Make the visibility to "gone" to prevent failures.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_new_sound_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index 60994d892b6e..ee8d02301d5d 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -144,10 +144,6 @@ Biometric UI.
Delegates SysUI events to WM Shell controllers.
-### [com.android.systemui.people.widget.PeopleSpaceWidgetEnabler](/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java)
-
-Enables People Space widgets.
-
---
* [Plugins](/packages/SystemUI/docs/plugins.md)
diff --git a/packages/SystemUI/docs/media-controls-pipeline.png b/packages/SystemUI/docs/media-controls-pipeline.png
new file mode 100644
index 000000000000..e7408ad9fd4a
--- /dev/null
+++ b/packages/SystemUI/docs/media-controls-pipeline.png
Binary files differ
diff --git a/packages/SystemUI/docs/media-controls.md b/packages/SystemUI/docs/media-controls.md
new file mode 100644
index 000000000000..579f453a3a92
--- /dev/null
+++ b/packages/SystemUI/docs/media-controls.md
@@ -0,0 +1,94 @@
+# SysUI Media Controls Pipeline
+
+[TOC]
+
+## Purpose
+
+Describe how events flow through the media controls pipeline, and provide a high level overview of what the different components do.
+
+## Pipeline Diagram
+
+![media controls pipeline](media-controls-pipeline.png)
+
+* Orange: External inputs
+* Blue: Internal listeners; all except `MediaDataManager` and `ResumeMediaBrowser` implement [`MediaDataManager.Listener`](/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt#711) and receive `onMediaDataLoaded` and `onMediaDataRemoved` events
+
+## Classes
+
+Files under [`systemui/media/`](/packages/SystemUI/src/com/android/systemui/media/):
+
+* UI
+ * `dialog/`
+ * Output switcher dialog (maintained by Settings team)
+ * IlluminationDrawable.kt
+ * LightSourceDrawable.kt
+ * These create the glow animation when you tap on a button (see [`qs_media_light_source`](/packages/SystemUI/res/drawable/qs_media_light_source.xml)). Should be reusable in other layouts.
+ * Carousel:
+ * MediaCarouselController.kt
+ * Keeps the carousel view up to date and handles state changes (e.g. expansion)
+ * Handles settings gear and page indicator
+ * MediaCarouselScrollHandler.kt
+ * Handles scrolling between players in the carousel
+ * MediaScrollView.kt
+ * Scrollview used in the carousel layout, has some custom measurement code
+ * Individual players:
+ * KeyguardMediaController.kt
+ * Lockscreen media controls have a special wrapper in order to work with the existing lockscreen notification layout
+ * MediaControlPanel.java
+ * Main class for media control UI
+ * SeekBarObserver.kt
+ * Updates seekbar state
+ * SeekBarViewModel.kt
+ * Implements its own `computePosition()` for the seekbar (to avoid continually polling the `PlaybackState`, which involves binder calls)
+ * Does some touch falsing (ignore flings, require drags to start near the thumb - otherwise users would often accidentally trigger the seekbar when they meant to move the carousel or shade)
+ * PlayerViewHolder.kt
+ * Holds references to the UI elements in the panel
+* Animation support:
+ * MediaHierarchyManager.kt
+ * Responsible for placement of media view and animation between hosts
+ * MediaHost.kt
+ * Every location that a media player could be located needs a `MediaHost`
+ * Tracks configuration (if it should show inactive media, needs falsing, etc.)
+ * MediaHostStatesManager.kt
+ * Manages the various media host states and coordinates heights between different players
+ * Has the most up to date state for any location
+ * MediaViewController.kt
+ * Controls a single instance of a media player, keeps the media view states up to date
+* Backend
+ * MediaData.kt
+ * Holds all the media data (track info, active/resume state, etc.)
+ * MediaDataCombineLatest.kt
+ * Combines update events from `MediaDataManager` and `MediaDeviceManager`, so that downstream listeners will have device info
+ * MediaDataFilter.kt
+ * Filters media data based on the current user
+ * Exit point for the pipeline: "external listeners" (currently `MediaHost` and `MediaCarouselController`), while they should be added via `MediaDataManager.addListener()`, will actually be listening to this output
+ * MediaDataManager.kt
+ * Entry point for the pipeline; initializes listener connections and assigns external listeners to the correct exit point
+ * Converts media notifications and resumable media info into `MediaData`
+ * MediaDeviceManager.kt
+ * Handles device updates
+ * MediaFeatureFlag.kt
+ * Utility to check whether media controls are enabled
+ * MediaSessionBasedFilter.kt
+ * Filters media events based on media session. This prevents duplicate controls in situations like casting where we might get both a local and remote object for the same media session.
+ * MediaTimeoutListener.kt
+ * Listens to `PlaybackState` and marks controls inactive after the media has been paused/stopped for 10 minutes (value can be adjusted locally with `adb shell setprop debug.sysui.media_timeout [ms]`)
+ * MediaResumeListener.kt
+ * Listens for new media data and attempts to find a valid `MediaBrowserService` for the app. If successful, sends the information back to the `MediaDataManager`
+ * Saves up to 5 valid `MediaBrowserService` components found this way, and queries them for recent media on boot or user change
+ * Note: the user can disable this feature completely (or block certain apps from being resumable) in [Settings](https://source.corp.google.com/android/packages/apps/Settings/src/com/android/settings/sound/ResumableMediaAppsController.java), in which case this listener will do nothing (or ignore updates from the blocked apps).
+ * ResumeMediaBrowser.java
+ * Connects to an app's [`MediaBrowser`](https://developer.android.com/reference/android/media/browse/MediaBrowser) to determine whether SystemUI is able to connect and find a recent [`MediaItem`](https://developer.android.com/reference/android/media/browse/MediaBrowser.MediaItem)
+* Factory classes (for unit testing):
+ * LocalMediaManagerFactory.kt
+ * MediaBrowserFactory.java
+ * MediaControllerFactory.java
+ * ResumeMediaBrowserFactory.java
+
+## Miscellaneous
+
+Other useful documents:
+
+* [go/sysui-media-resumption-requirements](https://goto.google.com/sysui-media-resumption-requirements) - Internal documentation for app developers about how to work with media resumption
+* [Playing nicely with media controls](https://android-developers.googleblog.com/2020/08/playing-nicely-with-media-controls.html) - blog post on the Android 11 updates
+* [Media Controls developer guide](https://developer.android.com/guide/topics/media/media-controls)
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index 79868093fb12..71cdaf5c7091 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index 04e645bd0a32..1e142eaeef86 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,13 +41,14 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
+ android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
new file mode 100644
index 000000000000..e09bf7e37ed0
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <bool name="can_use_one_handed_bouncer">true</bool>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 8d9d6ee68c67..6176f7c1dd0a 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,4 +22,5 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+ <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 187ae58c2f2c..cf9de5e7e226 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -85,10 +85,10 @@
android:tint="?attr/wallpaperTextColor" />
<ImageView
- android:id="@+id/alt_left_button"
+ android:id="@+id/wallet_button"
android:layout_height="@dimen/keyguard_affordance_height"
android:layout_width="@dimen/keyguard_affordance_width"
- android:layout_gravity="bottom|start"
+ android:layout_gravity="bottom|end"
android:scaleType="center"
android:tint="?attr/wallpaperTextColor"
android:layout_marginStart="24dp"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index e2f3e2a306e3..7ba28a8483c3 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -76,7 +76,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="42dp"
android:layout_marginHorizontal="48dp"
- android:adjustViewBounds="true"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/guideline"
@@ -110,6 +109,7 @@
android:visibility="invisible"
android:layout_width="200dp"
android:layout_height="200dp"
+ android:elevation="2dp"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintLeft_toLeftOf="parent"
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 13572fa9f4f3..db712e4b9f7a 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -17,6 +17,7 @@
<com.android.systemui.util.NeverExactlyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:clickable="true"
android:paddingBottom="@dimen/qs_tile_padding_top"
android:paddingTop="@dimen/qs_tile_padding_top"
diff --git a/packages/SystemUI/res/layout/udfps_animation_view.xml b/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
index 380dd855ffe1..0cfbf2e61dd1 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view.xml
+++ b/packages/SystemUI/res/layout/udfps_animation_view_bp.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.UdfpsAnimationViewBp
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.UdfpsAnimationViewBp>
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_fpm_other.xml b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
new file mode 100644
index 000000000000..f32faa0df867
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.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.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">
+</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/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3bc1c8053db3..b3fca366ea1e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -183,7 +183,9 @@
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
<!-- UDFPS colors -->
- <color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
+ <color name="udfps_enroll_icon">#000000</color> <!-- 100% black -->
+ <color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue -->
+ <color name="udfps_moving_target_stroke">#ff669DF6</color> <!-- 100% blue -->
<!-- Logout button -->
<color name="logout_button_bg_color">#ccffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0893c1488005..b75a0bc1525a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -91,9 +91,6 @@
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
- <!-- The number of columns in the Quick Settings customizer -->
- <integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer>
-
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">3</integer>
@@ -315,7 +312,6 @@
<item>com.android.systemui.accessibility.SystemActions</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
- <item>com.android.systemui.people.widget.PeopleSpaceWidgetEnabler</item>
</string-array>
<!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
@@ -416,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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5f8df5a13ad1..b5ded012b3bd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -294,8 +294,10 @@
<string name="screenrecord_share_label">Share</string>
<!-- A toast message shown after successfully canceling a screen recording [CHAR LIMIT=NONE] -->
<string name="screenrecord_cancel_success">Screen recording canceled</string>
- <!-- Notification text shown after saving a screen recording to prompt the user to view it [CHAR LIMIT=100] -->
- <string name="screenrecord_save_message">Screen recording saved, tap to view</string>
+ <!-- Notification text shown after saving a screen recording [CHAR LIMIT=100] -->
+ <string name="screenrecord_save_title">Screen recording saved</string>
+ <!-- Subtext for a notification shown after saving a screen recording to prompt the user to view it [CHAR_LIMIT=100] -->
+ <string name="screenrecord_save_text">Tap to view</string>
<!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
<string name="screenrecord_delete_error">Error deleting screen recording</string>
<!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2d202fb45bbc..ec26b8d7a6a8 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -608,6 +608,11 @@
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
+ <!-- Privacy dialog -->
+ <style name="PrivacyDialog" parent="ScreenRecord">
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ </style>
+
<!-- USB Contaminant dialog -->
<style name ="USBContaminant" />
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
index e5ced3ec3dc2..54242bece2b6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl
@@ -21,5 +21,5 @@ package com.android.systemui.shared.recents;
*/
oneway interface ISplitScreenListener {
void onStagePositionChanged(int stage, int position);
- void onTaskStageChanged(int taskId, int stage);
-} \ No newline at end of file
+ void onTaskStageChanged(int taskId, int stage, boolean visible);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 5c943f63a9a1..bac4c43ccddc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -19,6 +19,7 @@ package com.android.systemui.shared.recents;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
@@ -34,7 +35,7 @@ import com.android.systemui.shared.system.RemoteTransitionCompat;
/**
* Temporary callbacks into SystemUI.
- * Next id = 30
+ * Next id = 43
*/
interface ISystemUiProxy {
@@ -251,5 +252,7 @@ interface ISystemUiProxy {
void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
in Bundle options, in UserHandle user) = 40;
void startIntent(
- in PendingIntent intent, in int stage, in int position, in Bundle options) = 41;
+ in PendingIntent intent, in Intent fillInIntent, in int stage, in int position,
+ in Bundle options) = 41;
+ void removeFromSideStage(in int taskId) = 42;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index e6477f158c27..400bf15f8c65 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -113,17 +113,6 @@ public class RemoteAnimationAdapterCompat {
// TODO(bc-unlock): Build wrapped object for non-apps target.
final RemoteAnimationTargetCompat[] nonAppsCompat =
new RemoteAnimationTargetCompat[0];
- final Runnable animationFinishedCallback = new Runnable() {
- @Override
- public void run() {
- try {
- finishCallback.onTransitionFinished(null /* wct */);
- } catch (RemoteException e) {
- Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
- + " finished callback", e);
- }
- }
- };
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
boolean isReturnToHome = false;
@@ -143,8 +132,8 @@ public class RemoteAnimationAdapterCompat {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
- // Only deal with roots
- if (change.getParent() != null) continue;
+ // Only deal with independent layers
+ if (!TransitionInfo.isIndependent(change, info)) continue;
if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
t.setLayer(leash, info.getChanges().size() * 3 - i);
}
@@ -156,6 +145,18 @@ public class RemoteAnimationAdapterCompat {
}
}
t.apply();
+
+ final Runnable animationFinishedCallback = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ finishCallback.onTransitionFinished(null /* wct */);
+ } catch (RemoteException e) {
+ Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ + " finished callback", e);
+ }
+ }
+ };
// TODO(bc-unlcok): Pass correct transit type.
remoteAnimationAdapter.onAnimationStart(
TRANSIT_OLD_NONE,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 5f6fd30ffa1b..a2d7707a1569 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,12 +29,17 @@ import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.OrientationEventListener;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -55,6 +60,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -99,6 +105,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
+ private boolean mIsSecurityViewLeftAligned = true;
+ private boolean mOneHandedMode = false;
+ private SecurityMode mSecurityMode = SecurityMode.Invalid;
+ private ViewPropertyAnimator mRunningOneHandedAnimator;
+ private final OrientationEventListener mOrientationEventListener;
+
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -157,16 +169,20 @@ public class KeyguardSecurityContainer extends FrameLayout {
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+
void userActivity();
+
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
+
void reset();
+
void onCancelClicked();
}
@@ -224,12 +240,136 @@ public class KeyguardSecurityContainer extends FrameLayout {
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
+
+ mOrientationEventListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ updateLayoutForSecurityMode(mSecurityMode);
+ }
+ };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
+ updateLayoutForSecurityMode(securityMode);
+ mOrientationEventListener.enable();
+ }
+
+ void updateLayoutForSecurityMode(SecurityMode securityMode) {
+ mSecurityMode = securityMode;
+ mOneHandedMode = canUseOneHandedBouncer();
+
+ if (mOneHandedMode) {
+ mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
+ }
+
+ updateSecurityViewGravity();
+ updateSecurityViewLocation(false);
+ }
+
+ /** Return whether the one-handed keyguard should be enabled. */
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned(Context context) {
+ try {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private void updateSecurityViewGravity() {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+
+ if (mOneHandedMode) {
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ } else {
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ }
+
+ securityView.setLayoutParams(lp);
+ }
+
+ /**
+ * Moves the inner security view to the correct location (in one handed mode) with animation.
+ * This is triggered when the user taps on the side of the screen that is not currently occupied
+ * by the security view .
+ */
+ private void updateSecurityViewLocation(boolean animate) {
+ View securityView = findKeyguardSecurityView();
+
+ if (securityView == null) {
+ return;
+ }
+
+ if (!mOneHandedMode) {
+ securityView.setTranslationX(0);
+ return;
+ }
+
+ if (mRunningOneHandedAnimator != null) {
+ mRunningOneHandedAnimator.cancel();
+ mRunningOneHandedAnimator = null;
+ }
+
+ int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+
+ if (animate) {
+ mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+
+ mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mRunningOneHandedAnimator.start();
+ } else {
+ securityView.setTranslationX(targetTranslation);
+ }
+ }
+
+ @Nullable
+ private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+
+ if (isKeyguardSecurityView(child)) {
+ return (KeyguardSecurityViewFlipper) child;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isKeyguardSecurityView(View view) {
+ return view instanceof KeyguardSecurityViewFlipper;
}
public void onPause() {
@@ -238,6 +378,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+ mOrientationEventListener.disable();
}
@Override
@@ -319,19 +460,44 @@ public class KeyguardSecurityContainer extends FrameLayout {
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
+ } else {
+ if (!mIsDragging) {
+ handleTap(event);
+ }
}
}
return true;
}
+ private void handleTap(MotionEvent event) {
+ // If we're using a fullscreen security mode, skip
+ if (!mOneHandedMode) {
+ return;
+ }
+
+ // Did the tap hit the "other" side of the bouncer?
+ if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
+ || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
+ mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
+
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+
+ updateSecurityViewLocation(true);
+ }
+ }
+
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -441,18 +607,17 @@ public class KeyguardSecurityContainer extends FrameLayout {
return insets.inset(0, 0, 0, inset);
}
-
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -490,6 +655,47 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthMeasureSpec) / 2,
+ MeasureSpec.getMode(widthMeasureSpec));
+
+ for (int i = 0; i < getChildCount(); i++) {
+ final View view = getChildAt(i);
+ if (view.getVisibility() != GONE) {
+ if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ measureChildWithMargins(view, halfWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(view, widthMeasureSpec, 0,
+ heightMeasureSpec, 0);
+ }
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ maxWidth = Math.max(maxWidth,
+ view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ maxHeight = Math.max(maxHeight,
+ view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, view.getMeasuredState());
+ }
+ }
+
+ maxWidth += getPaddingLeft() + getPaddingRight();
+ maxHeight += getPaddingTop() + getPaddingBottom();
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1a8d420fb394..fdab8db67431 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,6 +404,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
+ mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index c77c86711abf..631c24844417 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,4 +92,13 @@ public class KeyguardSecurityModel {
throw new IllegalStateException("Unknown security quality:" + security);
}
}
+
+ /**
+ * Returns whether the given security view should be used in a "one handed" way. This can be
+ * used to change how the security view is drawn (e.g. take up less of the screen, and align to
+ * one side).
+ */
+ public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
+ return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
+ }
}
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/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index fe0ae33d17f1..ae4c8e5a3327 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -59,6 +59,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
@@ -246,6 +247,7 @@ public class Dependency {
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
@Inject Lazy<BatteryController> mBatteryController;
@Inject Lazy<NightDisplayListener> mNightDisplayListener;
+ @Inject Lazy<ReduceBrightColorsController> mReduceBrightColorsController;
@Inject Lazy<ManagedProfileController> mManagedProfileController;
@Inject Lazy<NextAlarmController> mNextAlarmController;
@Inject Lazy<DataSaverController> mDataSaverController;
@@ -393,6 +395,8 @@ public class Dependency {
mProviders.put(NightDisplayListener.class, mNightDisplayListener::get);
+ mProviders.put(ReduceBrightColorsController.class, mReduceBrightColorsController::get);
+
mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
mProviders.put(NextAlarmController.class, mNextAlarmController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 865ca40b1f4c..59c0fb816a96 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -19,15 +19,18 @@ package com.android.systemui;
import android.app.ActivityThread;
import android.app.Application;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.TimingsTraceLog;
import android.view.SurfaceControl;
@@ -37,6 +40,8 @@ import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.people.PeopleSpaceActivity;
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.shared.system.ThreadedRendererCompat;
import com.android.systemui.util.NotificationChannels;
@@ -121,6 +126,26 @@ public class SystemUIApplication extends Application implements
mServices[i].onBootCompleted();
}
}
+ // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider.
+ // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto)
+ try {
+ int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SHOW_PEOPLE_SPACE, 1);
+ context.getPackageManager().setComponentEnabledSetting(
+ new ComponentName(context, PeopleSpaceWidgetProvider.class),
+ showPeopleSpace == 1
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ context.getPackageManager().setComponentEnabledSetting(
+ new ComponentName(context, PeopleSpaceActivity.class),
+ showPeopleSpace == 1
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ } catch (Exception e) {
+ Log.w(TAG, "Error enabling People Space widget:", e);
+ }
}
}, bootCompletedFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 19520df08757..9d00262436e5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -115,7 +115,8 @@ public class SystemUIFactory {
.setShellCommandHandler(mWMComponent.getShellCommandHandler())
.setAppPairs(mWMComponent.getAppPairs())
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
- .setTransitions(mWMComponent.getTransitions());
+ .setTransitions(mWMComponent.getTransitions())
+ .setStartingSurface(mWMComponent.getStartingSurface());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -129,7 +130,8 @@ public class SystemUIFactory {
.setShellCommandHandler(Optional.ofNullable(null))
.setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
- .setTransitions(Transitions.createEmptyForTesting());
+ .setTransitions(Transitions.createEmptyForTesting())
+ .setStartingSurface(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (initializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
index 3bf75d105b9f..a51b6fd16445 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.View;
@@ -31,24 +32,32 @@ 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;
mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
+ mFingerprintDrawable.mutate();
}
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
- int margin = (int) (sensorRect.bottom - sensorRect.top) / 5;
- mFingerprintDrawable.setBounds(
- (int) sensorRect.left + margin,
+ final int margin = (int) sensorRect.height() / 8;
+
+ final Rect bounds = new Rect((int) sensorRect.left + margin,
(int) sensorRect.top + margin,
(int) sensorRect.right - margin,
(int) sensorRect.bottom - margin);
+ updateFingerprintIconBounds(bounds);
+ }
+
+ protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
+ mFingerprintDrawable.setBounds(bounds);
}
@Override
@@ -60,6 +69,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..015a598e972b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -22,13 +22,14 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.RectF;
-import android.util.Log;
+import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
/**
@@ -40,9 +41,13 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
private static final float SHADOW_RADIUS = 5.f;
private static final float PROGRESS_BAR_RADIUS = 140.f;
- @Nullable private RectF mSensorRect;
+ @NonNull private final Drawable mMovingTargetFpIcon;
@NonNull private final Paint mSensorPaint;
- private final int mNotificationShadeColor;
+ @NonNull private final Paint mBlueFill;
+ @NonNull private final Paint mBlueStroke;;
+
+ @Nullable private RectF mSensorRect;
+ @Nullable private UdfpsEnrollHelper mEnrollHelper;
UdfpsAnimationEnroll(@NonNull Context context) {
super(context);
@@ -53,23 +58,54 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK);
mSensorPaint.setStyle(Paint.Style.FILL);
- mNotificationShadeColor = Utils.getColorAttr(context,
- android.R.attr.colorBackgroundFloating).getDefaultColor();
+ mBlueFill = new Paint(0 /* flags */);
+ mBlueFill.setAntiAlias(true);
+ mBlueFill.setColor(context.getColor(R.color.udfps_moving_target_fill));
+ mBlueFill.setStyle(Paint.Style.FILL);
+
+ mBlueStroke = new Paint(0 /* flags */);
+ mBlueStroke.setAntiAlias(true);
+ mBlueStroke.setColor(context.getColor(R.color.udfps_moving_target_stroke));
+ mBlueStroke.setStyle(Paint.Style.STROKE);
+ mBlueStroke.setStrokeWidth(12);
+
+ mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
+ mMovingTargetFpIcon.setTint(Color.WHITE);
+ mMovingTargetFpIcon.mutate();
+ }
+
+ void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+ mEnrollHelper = helper;
}
@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;
}
@Override
+ protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
+ super.updateFingerprintIconBounds(bounds);
+ mMovingTargetFpIcon.setBounds(bounds);
+ }
+
+ @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) {
@@ -78,6 +114,24 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
}
}
mFingerprintDrawable.draw(canvas);
+
+ // Draw moving target
+ if (mEnrollHelper.isCenterEnrollmentComplete()) {
+ mFingerprintDrawable.setAlpha(64);
+
+ canvas.save();
+ final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
+ canvas.translate(point.x, point.y);
+ if (mSensorRect != null) {
+ canvas.drawOval(mSensorRect, mBlueFill);
+ canvas.drawOval(mSensorRect, mBlueStroke);
+ }
+
+ mMovingTargetFpIcon.draw(canvas);
+ canvas.restore();
+ } else {
+ mFingerprintDrawable.setAlpha(255);
+ }
}
@Override
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..43ecf6778022 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -20,40 +20,52 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.PointF;
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();
+
+ if (getUdfpsAnimation() != null) {
+ getUdfpsAnimation().onDestroy();
}
}
@@ -69,38 +81,46 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
return (int) ((1 - percent) * 255);
}
- void setParent(@NonNull UdfpsView parent) {
- mParent = parent;
+ void onIlluminationStarting() {
+ if (getUdfpsAnimation() == null) {
+ return;
+ }
+
+ getUdfpsAnimation().setIlluminationShowing(true);
+ postInvalidate();
}
- void setAnimation(@Nullable UdfpsAnimation animation) {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(null);
+ void onIlluminationStopped() {
+ if (getUdfpsAnimation() == null) {
+ return;
}
- mUdfpsAnimation = animation;
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(this);
- }
+ getUdfpsAnimation().setIlluminationShowing(false);
+ postInvalidate();
+ }
+
+ 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 +131,26 @@ 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();
+ }
+
+ /**
+ * @return the amount of translation needed if the view currently requires the user to touch
+ * somewhere other than the exact center of the sensor. For example, this can happen
+ * during guided enrollment.
+ */
+ @NonNull
+ PointF getTouchTranslation() {
+ return new PointF(0, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
new file mode 100644
index 000000000000..515b442b61f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.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 BiometricPrompt.
+ *
+ * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should
+ * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController
+ * is completed (inflate operation-specific views, instead of inflating generic udfps_view and
+ * adding operation-specific animations to it).
+ */
+public class UdfpsAnimationViewBp extends UdfpsAnimationView {
+ public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Nullable
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return null;
+ }
+}
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..543df33dd5d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.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 com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PointF;
+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 UdfpsAnimationEnroll 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;
+ mUdfpsAnimation.setEnrollHelper(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);
+ }
+
+ @NonNull
+ @Override
+ PointF getTouchTranslation() {
+ if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+ return new PointF(0, 0);
+ } else {
+ return mEnrollHelper.getNextGuidedEnrollmentPoint();
+ }
+ }
+}
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..852cdcbe20fd 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;
@@ -96,7 +97,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
public void showUdfpsOverlay(int sensorId, int reason) {
if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
|| reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
- mEnrollHelper = new UdfpsEnrollHelper(reason);
+ mEnrollHelper = new UdfpsEnrollHelper(mContext, reason);
} else {
mEnrollHelper = null;
}
@@ -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());
}
@@ -236,6 +231,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@Override
public void dozeTimeTick() {
+ if (mView == null) {
+ return;
+ }
mView.dozeTimeTick();
}
@@ -278,7 +276,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 +328,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 +352,40 @@ 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 view = (UdfpsAnimationViewEnroll)
+ inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
+ view.setEnrollHelper(mEnrollHelper);
+ return view;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_BP: {
+ final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp)
+ inflater.inflate(R.layout.udfps_animation_view_bp, null, false);
+ return view;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
+ final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard)
+ inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
+ view.setStatusBarStateController(mStatusBarStateController);
+ return view;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
+ final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
+ inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
+ return view;
+ }
+
default:
Log.d(TAG, "Animation for reason " + reason + " not supported yet");
return null;
@@ -371,6 +398,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..14eca1b1cb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -16,9 +16,15 @@
package com.android.systemui.biometrics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PointF;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.util.TypedValue;
-import androidx.annotation.NonNull;
+import java.util.ArrayList;
+import java.util.List;
/**
* Helps keep track of enrollment state and animates the progress bar accordingly.
@@ -26,35 +32,96 @@ import androidx.annotation.NonNull;
public class UdfpsEnrollHelper {
private static final String TAG = "UdfpsEnrollHelper";
+ // Enroll with two center touches before going to guided enrollment
+ private static final int NUM_CENTER_TOUCHES = 2;
+
+ interface Listener {
+ void onEnrollmentProgress(int remaining, int totalSteps);
+ }
+
// IUdfpsOverlayController reason
private final int mEnrollReason;
+ private final List<PointF> mGuidedEnrollmentPoints;
private int mTotalSteps = -1;
- private int mCurrentProgress = 0;
+ private int mRemainingSteps = -1;
+
+ // Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the
+ // interface makes no promises about monotonically increasing by one each time.
+ private int mLocationsEnrolled = 0;
+
+ @Nullable Listener mListener;
- public UdfpsEnrollHelper(int reason) {
+ public UdfpsEnrollHelper(@NonNull Context context, int reason) {
mEnrollReason = reason;
+ mGuidedEnrollmentPoints = new ArrayList<>();
+
+ // Number of pixels per mm
+ float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
+ context.getResources().getDisplayMetrics());
+
+ mGuidedEnrollmentPoints.add(new PointF( 2.00f * px, 0.00f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, 1.31f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 0.88f * px, 2.70f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-3.62f * px, 2.54f * px));
+ mGuidedEnrollmentPoints.add(new PointF(-1.49f * px, 5.57f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 2.29f * px, 4.92f * px));
+ mGuidedEnrollmentPoints.add(new PointF( 3.82f * px, 1.78f * px));
}
boolean shouldShowProgressBar() {
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 (remaining != mRemainingSteps) {
+ mLocationsEnrolled++;
+ }
+
+ mRemainingSteps = remaining;
- void updateProgress(@NonNull UdfpsProgressBar progressBar) {
- progressBar.setProgress(mCurrentProgress);
+ if (mListener != null) {
+ mListener.onEnrollmentProgress(remaining, mTotalSteps);
+ }
}
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) {
+ mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
+ }
+ }
+
+ boolean isCenterEnrollmentComplete() {
+ if (mTotalSteps == -1 || mRemainingSteps == -1) {
+ return false;
+ }
+ final int stepsEnrolled = mTotalSteps - mRemainingSteps;
+ return stepsEnrolled >= NUM_CENTER_TOUCHES;
+ }
+
+ @NonNull
+ PointF getNextGuidedEnrollmentPoint() {
+ final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
+ return mGuidedEnrollmentPoints.get(index % mGuidedEnrollmentPoints.size());
+ }
}
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..75a362131de1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,12 +27,12 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PointF;
import android.graphics.RectF;
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 +51,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 +65,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 +82,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 +92,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 +117,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void dozeTimeTick() {
+ if (mAnimationView == null) {
+ return;
+ }
mAnimationView.dozeTimeTick();
}
@@ -143,12 +131,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
@@ -195,8 +181,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
boolean isValidTouch(float x, float y, float pressure) {
// The X and Y coordinates of the sensor's center.
- final float cx = mSensorRect.centerX();
- final float cy = mSensorRect.centerY();
+ final PointF translation = mAnimationView.getTouchTranslation();
+ final float cx = mSensorRect.centerX() + translation.x;
+ final float cy = mSensorRect.centerY() + translation.y;
// Radii along the X and Y axes.
final float rx = (mSensorRect.right - mSensorRect.left) / 2.0f;
final float ry = (mSensorRect.bottom - mSensorRect.top) / 2.0f;
@@ -229,7 +216,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 +224,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/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 726e2d06bf90..a2f96bbad203 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.om.OverlayManager;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.ColorDisplayManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -60,6 +61,7 @@ import com.android.systemui.navigationbar.NavigationBarOverlayController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserTracker;
@@ -82,6 +84,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.theme.ThemeOverlayApplier;
import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -266,6 +269,15 @@ public class DependencyProvider {
}
/** */
+ @SysUISingleton
+ @Provides
+ public ReduceBrightColorsController provideReduceBrightColorsListener(
+ @Background Handler bgHandler, UserTracker userTracker,
+ ColorDisplayManager colorDisplayManager, SecureSettings secureSettings) {
+ return new ReduceBrightColorsController(userTracker, bgHandler,
+ colorDisplayManager, secureSettings);
+ }
+
@Provides
@SysUISingleton
public ActivityManagerWrapper provideActivityManagerWrapper() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 04c4e977b2cf..1c5715c0296d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -25,6 +25,7 @@ import android.app.IActivityTaskManager;
import android.app.IWallpaperManager;
import android.app.KeyguardManager;
import android.app.NotificationManager;
+import android.app.StatsManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
@@ -321,6 +322,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static StatsManager provideStatsManager(Context context) {
+ return context.getSystemService(StatsManager.class);
+ }
+
+ @Provides
+ @Singleton
@Nullable
static TelecomManager provideTelecomManager(Context context) {
return context.getSystemService(TelecomManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index ffb8446f3e21..8f79de518419 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -33,6 +33,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
@@ -88,6 +89,9 @@ public interface SysUIComponent {
@BindsInstance
Builder setTransitions(RemoteTransitions t);
+ @BindsInstance
+ Builder setStartingSurface(Optional<StartingSurface> s);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 5d226d562c29..1ed881993800 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -27,7 +27,6 @@ import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.media.systemsounds.HomeSoundEffectController;
-import com.android.systemui.people.widget.PeopleSpaceWidgetEnabler;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.television.TvOngoingPrivacyChip;
import com.android.systemui.recents.Recents;
@@ -185,10 +184,4 @@ public abstract class SystemUIBinder {
@IntoMap
@ClassKey(HomeSoundEffectController.class)
public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
-
- /** Inject into PeopleSpaceWidgetEnabler. */
- @Binds
- @IntoMap
- @ClassKey(PeopleSpaceWidgetEnabler.class)
- public abstract SystemUI bindPeopleSpaceWidgetEnabler(PeopleSpaceWidgetEnabler sysui);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index f3726a37bb65..1b77d1c16639 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -32,6 +32,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
@@ -98,4 +99,7 @@ public interface WMComponent {
@WMSingleton
RemoteTransitions getTransitions();
+
+ @WMSingleton
+ Optional<StartingSurface> getStartingSurface();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index bd3f5a6d82a5..7fb7d8b0eaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -20,9 +20,13 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.text.TextUtils
+import android.util.Log
import com.android.settingslib.media.MediaOutputConstants
import javax.inject.Inject
+private const val TAG = "MediaOutputDlgReceiver"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
/**
* BroadcastReceiver for handling media output intent
*/
@@ -32,8 +36,13 @@ class MediaOutputDialogReceiver @Inject constructor(
override fun onReceive(context: Context, intent: Intent) {
if (TextUtils.equals(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
intent.action)) {
- mediaOutputDialogFactory.create(
- intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME), false)
+ val packageName: String? =
+ intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
+ if (!TextUtils.isEmpty(packageName)) {
+ mediaOutputDialogFactory.create(packageName!!, false)
+ } else if (DEBUG) {
+ Log.e(TAG, "Unable to launch media output dialog. Package name is empty.")
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 70b7b047eebc..4491cc12a3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -180,7 +180,7 @@ public class NavigationBarController implements Callbacks,
}
} else {
for (int i = 0; i < mNavigationBars.size(); i++) {
- mNavigationBars.get(i).onConfigurationChanged(newConfig);
+ mNavigationBars.valueAt(i).onConfigurationChanged(newConfig);
}
}
}
@@ -193,7 +193,7 @@ public class NavigationBarController implements Callbacks,
if (navBar == null) {
continue;
}
- NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView();
+ NavigationBarView view = (NavigationBarView) navBar.getView();
if (view != null) {
view.updateStates();
}
@@ -399,7 +399,7 @@ public class NavigationBarController implements Callbacks,
if (i > 0) {
pw.println();
}
- mNavigationBars.get(i).dump(pw);
+ mNavigationBars.valueAt(i).dump(pw);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 19e32783f765..35d5ca949f85 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -279,9 +279,11 @@ public class NavigationBarView extends FrameLayout implements
return;
}
+ // When in gestural and the IME is showing, don't use the nearest region since it will take
+ // gesture space away from the IME
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(getButtonLocations(false /* includeFloatingRotationButton */,
- false /* inScreen */));
+ false /* inScreen */, false /* useNearestRegion */));
};
private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
@@ -981,7 +983,8 @@ public class NavigationBarView extends FrameLayout implements
*/
public void notifyActiveTouchRegions() {
mOverviewProxyService.onActiveNavBarRegionChanges(
- getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */));
+ getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */,
+ true /* useNearestRegion */));
}
private void updateButtonTouchRegionCache() {
@@ -992,35 +995,49 @@ public class NavigationBarView extends FrameLayout implements
.findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
}
+ /**
+ * @param includeFloatingRotationButton Whether to include the floating rotation button in the
+ * region for all the buttons
+ * @param inScreenSpace Whether to return values in screen space or window space
+ * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
+ * @return
+ */
private Region getButtonLocations(boolean includeFloatingRotationButton,
- boolean inScreenSpace) {
+ boolean inScreenSpace, boolean useNearestRegion) {
+ if (useNearestRegion && !inScreenSpace) {
+ // We currently don't support getting the nearest region in anything but screen space
+ useNearestRegion = false;
+ }
mTmpRegion.setEmpty();
updateButtonTouchRegionCache();
- updateButtonLocation(getBackButton(), inScreenSpace);
- updateButtonLocation(getHomeButton(), inScreenSpace);
- updateButtonLocation(getRecentsButton(), inScreenSpace);
- updateButtonLocation(getImeSwitchButton(), inScreenSpace);
- updateButtonLocation(getAccessibilityButton(), inScreenSpace);
+ updateButtonLocation(getBackButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(getHomeButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(getRecentsButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(getImeSwitchButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(getAccessibilityButton(), inScreenSpace, useNearestRegion);
if (includeFloatingRotationButton && mFloatingRotationButton.isVisible()) {
+ // Note: this button is floating so the nearest region doesn't apply
updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace);
} else {
- updateButtonLocation(getRotateSuggestionButton(), inScreenSpace);
+ updateButtonLocation(getRotateSuggestionButton(), inScreenSpace, useNearestRegion);
}
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()
&& mNavBarOverlayController.isVisible()) {
+ // Note: this button is floating so the nearest region doesn't apply
updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace);
}
return mTmpRegion;
}
- private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace) {
+ private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace,
+ boolean useNearestRegion) {
View view = button.getCurrentView();
if (view == null || !button.isVisible()) {
return;
}
// If the button is tappable from perspective of NearestTouchFrame, then we'll
// include the regions where the tap is valid instead of just the button layout location
- if (mButtonFullTouchableRegions.containsKey(view)) {
+ if (useNearestRegion && mButtonFullTouchableRegions.containsKey(view)) {
mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index 870e3bed2b7a..422ffd524aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -200,7 +200,7 @@ public class NavigationModeController implements Dumpable {
Log.d(TAG, " assetPaths=");
ApkAssets[] assets = context.getResources().getAssets().getApkAssets();
for (ApkAssets a : assets) {
- Log.d(TAG, " " + a.getAssetPath());
+ Log.d(TAG, " " + a.getDebugName());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 2ea8657f88e5..a69ec278be91 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -23,7 +23,6 @@ import android.app.Activity;
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
-import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
@@ -34,12 +33,10 @@ import android.provider.Settings;
import android.util.Log;
import android.view.ViewGroup;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
@@ -54,15 +51,14 @@ public class PeopleSpaceActivity extends Activity {
private ViewGroup mPeopleSpaceLayout;
private IPeopleManager mPeopleManager;
+ private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private INotificationManager mNotificationManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private Context mContext;
- private AppWidgetManager mAppWidgetManager;
private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
- private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@Inject
public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) {
@@ -81,8 +77,8 @@ public class PeopleSpaceActivity extends Activity {
mPackageManager = getPackageManager();
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
+ mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(mContext);
mLauncherApps = mContext.getSystemService(LauncherApps.class);
- mAppWidgetManager = AppWidgetManager.getInstance(mContext);
setTileViewsWithPriorityConversations();
mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
INVALID_APPWIDGET_ID);
@@ -142,29 +138,13 @@ public class PeopleSpaceActivity extends Activity {
+ mAppWidgetId);
}
}
-
- PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
- int[] widgetIds = new int[mAppWidgetId];
- // TODO: Populate new widget with existing conversation notification, if there is any.
- PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
- mPeopleManager);
- if (mLauncherApps != null) {
- try {
- if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
- mLauncherApps.cacheShortcuts(tile.getPackageName(),
- Collections.singletonList(tile.getId()),
- tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
- } catch (Exception e) {
- Log.w(TAG, "Exception caching shortcut:" + e);
- }
- }
+ mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
finishActivity();
}
/** Finish activity with a successful widget configuration result. */
private void finishActivity() {
if (DEBUG) Log.d(TAG, "Widget added!");
- mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
setActivityResult(RESULT_OK);
finish();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 41080afb3604..f9a16c1c1970 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -220,7 +220,7 @@ public class PeopleSpaceUtils {
}
@Nullable
- private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+ public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
AppWidgetManager appWidgetManager,
Context context, int appWidgetId) {
try {
@@ -230,7 +230,7 @@ public class PeopleSpaceUtils {
String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
- if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+ if (!validKey(shortcutId, pkg, userId)) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
shortcutId = sp.getString(String.valueOf(appWidgetId), null);
if (shortcutId == null) {
@@ -268,6 +268,17 @@ public class PeopleSpaceUtils {
}
}
+ /** Returns stored widgets for the conversation specified. */
+ public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId,
+ String packageName, int userId) {
+ if (shortcutId == null || packageName == null) {
+ return new HashSet<>();
+ }
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ return new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ }
+
+
/** Best-effort attempts to migrate existing users to the new storage format. */
// TODO: Remove after sufficient time. Temporary migration storage for existing users.
private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
@@ -286,7 +297,11 @@ public class PeopleSpaceUtils {
if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
setStorageForTile(context, entry.get(), appWidgetId);
} else {
- Log.e(TAG, "Could not migrate user");
+ Log.e(TAG, "Could not migrate user. Delete old storage");
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.remove(String.valueOf(appWidgetId));
+ editor.apply();
}
} catch (Exception e) {
Log.e(TAG, "Could not query conversations");
@@ -320,17 +335,10 @@ public class PeopleSpaceUtils {
}
/** Removes stored data when tile is deleted. */
- public static void removeStorageForTile(Context context, int widgetId) {
- SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
- Context.MODE_PRIVATE);
- String packageName = widgetSp.getString(PACKAGE_NAME, null);
- String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
- int userId = widgetSp.getInt(USER_ID, -1);
-
+ public static void removeStorageForTile(Context context, String key, int widgetId) {
// Delete widgetId mapping to key.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sp.edit();
- String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
storedWidgetIds.remove(String.valueOf(widgetId));
editor.putStringSet(key, storedWidgetIds);
@@ -338,6 +346,8 @@ public class PeopleSpaceUtils {
editor.apply();
// Delete all data specifically mapped to widgetId.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
SharedPreferences.Editor widgetEditor = widgetSp.edit();
widgetEditor.remove(PACKAGE_NAME);
widgetEditor.remove(USER_ID);
@@ -345,21 +355,6 @@ public class PeopleSpaceUtils {
widgetEditor.apply();
}
- /**
- * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
- * requested update data.
- */
- public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
- String packageName, int userId) {
- SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
- Context.MODE_PRIVATE);
- String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
- int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
- String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
- return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
- && storedUserId == userId;
- }
-
static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
if (notificationEntryManager == null) {
@@ -396,39 +391,8 @@ public class PeopleSpaceUtils {
return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
}
- /**
- * If incoming notification changed tile, store the changes in the tile options.
- */
- public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
- Context context,
- StatusBarNotification sbn,
- NotificationAction notificationAction, AppWidgetManager appWidgetManager,
- int appWidgetId) {
- PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
- appWidgetId);
- if (storedTile == null) {
- if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
- return;
- }
- if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
- if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
- storedTile = augmentTileFromNotification(context, storedTile, sbn);
- } else {
- if (DEBUG) {
- Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
- }
- storedTile = storedTile
- .toBuilder()
- .setNotificationKey(null)
- .setNotificationContent(null)
- .setNotificationDataUri(null)
- .setNotificationCategory(null)
- .build();
- }
- updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
- }
-
- static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
+ /** Augments {@code tile} with the notification content from {@code sbn}. */
+ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile,
StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification == null) {
@@ -992,7 +956,7 @@ public class PeopleSpaceUtils {
}
/** Update app widget options and the current view. */
- private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
+ public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile) {
updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
RemoteViews views = createRemoteViews(context, tile, appWidgetId);
@@ -1065,10 +1029,19 @@ public class PeopleSpaceUtils {
* <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
* </ul>
*/
+ @Nullable
public static String getKey(String shortcutId, String packageName, int userId) {
+ if (!validKey(shortcutId, packageName, userId)) {
+ return null;
+ }
return shortcutId + "/" + userId + "/" + packageName;
}
+ /** Returns whether the key is valid. */
+ public static boolean validKey(String shortcutId, String packageName, int userId) {
+ return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0;
+ }
+
/** Returns the userId associated with a {@link PeopleSpaceTile} */
public static int getUserId(PeopleSpaceTile tile) {
return tile.getUserHandle().getIdentifier();
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
deleted file mode 100644
index 3df264421d75..000000000000
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.people.widget;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.util.Log;
-
-import com.android.systemui.SystemUI;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.people.PeopleSpaceActivity;
-import com.android.systemui.statusbar.FeatureFlags;
-
-import javax.inject.Inject;
-
-/**
- * Enables People Space widgets.
- */
-@SysUISingleton
-public class PeopleSpaceWidgetEnabler extends SystemUI {
- private static final String TAG = "PeopleSpaceWdgtEnabler";
- private Context mContext;
- private FeatureFlags mFeatureFlags;
-
- @Inject
- public PeopleSpaceWidgetEnabler(Context context, FeatureFlags featureFlags) {
- super(context);
- mContext = context;
- mFeatureFlags = featureFlags;
- }
-
- @Override
- public void start() {
- Log.d(TAG, "Starting service");
- try {
- boolean showPeopleSpace = mFeatureFlags.isPeopleTileEnabled();
- mContext.getPackageManager().setComponentEnabledSetting(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class),
- showPeopleSpace
- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- mContext.getPackageManager().setComponentEnabledSetting(
- new ComponentName(mContext, PeopleSpaceActivity.class),
- showPeopleSpace
- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- } catch (Exception e) {
- Log.w(TAG, "Error enabling People Space widget:", e);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 9e5c786b9a63..22ee9e89d0a0 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,12 +16,28 @@
package com.android.systemui.people.widget;
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
+import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification;
+import static com.android.systemui.people.PeopleSpaceUtils.getPeopleSpaceTile;
+import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds;
+import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
+
import android.app.NotificationChannel;
+import android.app.Person;
+import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
+import android.app.people.PeopleManager;
+import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
@@ -30,14 +46,18 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.appwidget.IAppWidgetService;
-import com.android.systemui.R;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
@@ -49,52 +69,49 @@ public class PeopleSpaceWidgetManager {
private static final String TAG = "PeopleSpaceWidgetMgr";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+ private final Object mLock = new Object();
private final Context mContext;
- private IAppWidgetService mAppWidgetService;
+ private LauncherApps mLauncherApps;
private AppWidgetManager mAppWidgetManager;
- private IPeopleManager mPeopleManager;
+ private IPeopleManager mIPeopleManager;
+ private SharedPreferences mSharedPrefs;
+ private PeopleManager mPeopleManager;
+ public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ @GuardedBy("mLock")
+ public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners =
+ new HashMap<>();
@Inject
- public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
+ public PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
mContext = context;
- mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
- mPeopleManager = IPeopleManager.Stub.asInterface(
+ mIPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
- }
-
- /**
- * Constructor used for testing.
- */
- @VisibleForTesting
- protected PeopleSpaceWidgetManager(Context context) {
- if (DEBUG) Log.d(TAG, "constructor");
- mContext = context;
- mAppWidgetService = IAppWidgetService.Stub.asInterface(
- ServiceManager.getService(Context.APPWIDGET_SERVICE));
+ mLauncherApps = context.getSystemService(LauncherApps.class);
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ mPeopleManager = mContext.getSystemService(PeopleManager.class);
}
/**
* AppWidgetManager setter used for testing.
*/
@VisibleForTesting
- protected void setAppWidgetManager(IAppWidgetService appWidgetService,
- AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
- mAppWidgetService = appWidgetService;
+ protected void setAppWidgetManager(
+ AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
+ PeopleManager peopleManager, LauncherApps launcherApps) {
mAppWidgetManager = appWidgetManager;
+ mIPeopleManager = iPeopleManager;
mPeopleManager = peopleManager;
+ mLauncherApps = launcherApps;
}
/**
* Updates People Space widgets.
*/
- public void updateWidgets() {
+ public void updateWidgets(int[] widgetIds) {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
- int[] widgetIds = mAppWidgetService.getAppWidgetIds(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
- );
if (widgetIds.length == 0) {
if (DEBUG) Log.d(TAG, "no widgets to update");
return;
@@ -103,14 +120,11 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
-
if (showSingleConversation) {
- PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mPeopleManager);
- } else {
- mAppWidgetService
- .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
- R.id.widget_list_view);
+ synchronized (mLock) {
+ PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
+ mAppWidgetManager, mIPeopleManager);
+ }
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
@@ -121,9 +135,9 @@ public class PeopleSpaceWidgetManager {
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
- public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
+ public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
+ if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
@@ -135,26 +149,22 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Sbn shortcut id is null");
return;
}
- int[] widgetIds = mAppWidgetService.getAppWidgetIds(
+ int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
Log.d(TAG, "No app widget ids returned");
return;
}
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
- String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
- Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
- if (storedWidgetIds.isEmpty()) {
- Log.d(TAG, "No stored widget ids");
- return;
- }
- for (String widgetIdString : storedWidgetIds) {
- int widgetId = Integer.parseInt(widgetIdString);
- if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
- sbn, notificationAction, mAppWidgetManager, widgetId);
+ synchronized (mLock) {
+ Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId,
+ sbn.getPackageName(),
+ UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier());
+ for (String widgetIdString : storedWidgetIds) {
+ int widgetId = Integer.parseInt(widgetIdString);
+ if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
+ updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId);
+ }
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
@@ -162,6 +172,91 @@ public class PeopleSpaceWidgetManager {
}
/**
+ * Update the tiles associated with the incoming conversation update.
+ */
+ public void updateWidgetsWithConversationChanged(ConversationChannel conversation) {
+ ShortcutInfo info = conversation.getShortcutInfo();
+ synchronized (mLock) {
+ Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(),
+ info.getPackage(),
+ info.getUserId());
+ for (String widgetIdString : storedWidgetIds) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Conversation update for widget " + widgetIdString + " , "
+ + info.getLabel());
+ }
+ updateStorageAndViewWithConversationData(conversation,
+ Integer.valueOf(widgetIdString));
+ }
+ }
+ }
+
+ /**
+ * Update {@code appWidgetId} with the new data provided by {@code conversation}.
+ */
+ private void updateStorageAndViewWithConversationData(ConversationChannel conversation,
+ int appWidgetId) {
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
+ mContext,
+ appWidgetId);
+ if (storedTile == null) {
+ if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to");
+ return;
+ }
+ ShortcutInfo info = conversation.getShortcutInfo();
+ Uri uri = null;
+ if (info.getPersons() != null && info.getPersons().length > 0) {
+ Person person = info.getPersons()[0];
+ uri = person.getUri() == null ? null : Uri.parse(person.getUri());
+ }
+ storedTile = storedTile.toBuilder()
+ .setUserName(info.getLabel())
+ .setUserIcon(
+ PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable(
+ info, 0))
+ )
+ .setContactUri(uri)
+ .setStatuses(conversation.getStatuses())
+ .setLastInteractionTimestamp(conversation.getLastEventTimestamp())
+ .setIsImportantConversation(conversation.getParentNotificationChannel() != null
+ && conversation.getParentNotificationChannel().isImportantConversation())
+ .build();
+ updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
+ }
+
+ /**
+ * Update {@code appWidgetId} with the new data provided by {@code sbn}.
+ */
+ private void updateStorageAndViewWithNotificationData(
+ StatusBarNotification sbn,
+ PeopleSpaceUtils.NotificationAction notificationAction,
+ int appWidgetId) {
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager,
+ mContext,
+ appWidgetId);
+ if (storedTile == null) {
+ if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
+ return;
+ }
+ if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
+ if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
+ storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
+ }
+ storedTile = storedTile
+ .toBuilder()
+ .setNotificationKey(null)
+ .setNotificationContent(null)
+ .setNotificationDataUri(null)
+ .build();
+ }
+ updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile);
+ }
+
+ /**
* Attaches the manager to the pipeline, making it ready to receive events. Should only be
* called once.
*/
@@ -174,8 +269,7 @@ public class PeopleSpaceWidgetManager {
@Override
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
- if (DEBUG) Log.d(TAG, "onNotificationPosted");
- updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
+ updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -183,8 +277,7 @@ public class PeopleSpaceWidgetManager {
StatusBarNotification sbn,
NotificationListenerService.RankingMap rankingMap
) {
- if (DEBUG) Log.d(TAG, "onNotificationRemoved");
- updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
+ updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -192,8 +285,7 @@ public class PeopleSpaceWidgetManager {
StatusBarNotification sbn,
NotificationListenerService.RankingMap rankingMap,
int reason) {
- if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
- updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
+ updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -204,7 +296,6 @@ public class PeopleSpaceWidgetManager {
@Override
public void onNotificationsInitialized() {
if (DEBUG) Log.d(TAG, "onNotificationsInitialized");
- updateWidgets();
}
@Override
@@ -213,11 +304,131 @@ public class PeopleSpaceWidgetManager {
UserHandle user,
NotificationChannel channel,
int modificationType) {
- if (DEBUG) Log.d(TAG, "onNotificationChannelModified");
if (channel.isConversation()) {
- updateWidgets();
+ updateWidgets(mAppWidgetManager.getAppWidgetIds(
+ new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
+ ));
}
}
};
-} \ No newline at end of file
+ /** Adds {@code tile} mapped to {@code appWidgetId}. */
+ public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) {
+ mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED);
+ synchronized (mLock) {
+ if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName());
+ PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId);
+ }
+ try {
+ if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
+ mLauncherApps.cacheShortcuts(tile.getPackageName(),
+ Collections.singletonList(tile.getId()),
+ tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception caching shortcut:" + e);
+ }
+ PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider();
+ provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId});
+ }
+
+ /** Registers a conversation listener for {@code appWidgetId} if not already registered. */
+ public void registerConversationListenerIfNeeded(int widgetId,
+ PeopleSpaceWidgetProvider.TileConversationListener newListener) {
+ // Retrieve storage needed for registration.
+ String packageName;
+ String shortcutId;
+ int userId;
+ String key;
+ synchronized (mLock) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ packageName = widgetSp.getString(PACKAGE_NAME, null);
+ shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ if (key == null) {
+ if (DEBUG) Log.e(TAG, "Could not register " + widgetId);
+ return;
+ }
+ }
+ synchronized (mListeners) {
+ if (mListeners.containsKey(key)) {
+ if (DEBUG) Log.d(TAG, "Already registered listener");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key);
+ mListeners.put(key, newListener);
+ }
+ mPeopleManager.registerConversationListener(packageName,
+ userId,
+ shortcutId, newListener,
+ mContext.getMainExecutor());
+ }
+
+ /** Deletes all storage, listeners, and caching for {@code appWidgetIds}. */
+ public void deleteWidgets(int[] appWidgetIds) {
+ for (int widgetId : appWidgetIds) {
+ if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId);
+ mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ // Retrieve storage needed for widget deletion.
+ String packageName;
+ String shortcutId;
+ int userId;
+ String key;
+ Set<String> storedWidgetIdsForKey;
+ synchronized (mLock) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ packageName = widgetSp.getString(PACKAGE_NAME, null);
+ shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ if (key == null) {
+ if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
+ return;
+ }
+ storedWidgetIdsForKey = new HashSet<>(
+ mSharedPrefs.getStringSet(key, new HashSet<>()));
+ }
+ synchronized (mLock) {
+ PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId);
+ }
+ // If another tile with the conversation is still stored, we need to keep the listener.
+ if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString());
+ if (storedWidgetIdsForKey.contains(String.valueOf(widgetId))
+ && storedWidgetIdsForKey.size() == 1) {
+ if (DEBUG) Log.d(TAG, "Remove caching and listener");
+ unregisterConversationListener(key, widgetId);
+ uncacheConversationShortcut(shortcutId, packageName, userId);
+ }
+ }
+ }
+
+ /** Unregisters the conversation listener for {@code appWidgetId}. */
+ private void unregisterConversationListener(String key, int appWidgetId) {
+ PeopleSpaceWidgetProvider.TileConversationListener registeredListener;
+ synchronized (mListeners) {
+ registeredListener = mListeners.get(key);
+ if (registeredListener == null) {
+ if (DEBUG) Log.d(TAG, "Cannot find listener to unregister");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key);
+ mListeners.remove(key);
+ }
+ mPeopleManager.unregisterConversationListener(registeredListener);
+ }
+
+ /** Uncaches the conversation shortcut. */
+ private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) {
+ try {
+ if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
+ mLauncherApps.uncacheShortcuts(packageName,
+ Collections.singletonList(shortcutId),
+ UserHandle.of(userId),
+ LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+ } catch (Exception e) {
+ Log.d(TAG, "Exception uncaching shortcut:" + e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 90baf56e0137..c0c18471ba32 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,31 +16,17 @@
package com.android.systemui.people.widget;
-import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
-import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
-import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
-
-import android.app.PendingIntent;
-import android.app.people.IPeopleManager;
+import android.annotation.NonNull;
+import android.app.people.ConversationChannel;
+import android.app.people.PeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherApps;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
-import android.widget.RemoteViews;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.people.PeopleSpaceUtils;
-import java.util.Collections;
-
/** People Space Widget Provider class. */
public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
private static final String TAG = "PeopleSpaceWidgetPvd";
@@ -50,7 +36,26 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
public static final String EXTRA_USER_HANDLE = "extra_user_handle";
- public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+ public PeopleSpaceWidgetManager peopleSpaceWidgetManager;
+
+ /** Listener for the shortcut data changes. */
+ public class TileConversationListener implements PeopleManager.ConversationListener {
+
+ @Override
+ public void onConversationUpdate(@NonNull ConversationChannel conversation) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Received updated conversation: "
+ + conversation.getShortcutInfo().getLabel());
+ }
+ if (peopleSpaceWidgetManager == null) {
+ // This shouldn't happen since onUpdate is called at reboot.
+ Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized");
+ return;
+ }
+ peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation);
+ }
+ }
/** Called when widget updates. */
@Override
@@ -58,70 +63,32 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
super.onUpdate(context, appWidgetManager, appWidgetIds);
if (DEBUG) Log.d(TAG, "onUpdate called");
- boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
- if (showSingleConversation) {
- PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
- appWidgetManager, IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE)));
- return;
- }
- // Perform this loop procedure for each App Widget that belongs to this provider
+ ensurePeopleSpaceWidgetManagerInitialized(context);
+ peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
for (int appWidgetId : appWidgetIds) {
- RemoteViews views =
- new RemoteViews(context.getPackageName(), R.layout.people_space_widget);
-
- Intent intent = new Intent(context, PeopleSpaceWidgetService.class);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- views.setRemoteAdapter(R.id.widget_list_view, intent);
-
- Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
- activityIntent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NO_HISTORY
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- PendingIntent pendingIntent = PendingIntent.getActivity(
- context,
- appWidgetId,
- activityIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent);
+ PeopleSpaceWidgetProvider.TileConversationListener
+ newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
+ peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
+ newListener);
+ }
+ return;
+ }
- // Tell the AppWidgetManager to perform an update on the current app widget
- appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
- appWidgetManager.updateAppWidget(appWidgetId, views);
+ private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
+ if (peopleSpaceWidgetManager == null) {
+ peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-
- for (int widgetId : appWidgetIds) {
- if (DEBUG) Log.d(TAG, "Widget removed");
- mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
- if (launcherApps != null) {
- SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
- Context.MODE_PRIVATE);
- String packageName = widgetSp.getString(PACKAGE_NAME, null);
- String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
- int userId = widgetSp.getInt(USER_ID, -1);
+ ensurePeopleSpaceWidgetManagerInitialized(context);
+ peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
+ }
- if (packageName != null && shortcutId != null && userId != -1) {
- try {
- if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId);
- launcherApps.uncacheShortcuts(packageName,
- Collections.singletonList(shortcutId),
- UserHandle.of(userId),
- LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
- } catch (Exception e) {
- Log.d(TAG, "Exception uncaching shortcut:" + e);
- }
- }
- }
- PeopleSpaceUtils.removeStorageForTile(context, widgetId);
- }
+ @VisibleForTesting
+ public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
+ peopleSpaceWidgetManager = manager;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 680a617e4d26..8ec9b682ffdc 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -47,7 +47,7 @@ class PrivacyDialog(
context: Context,
private val list: List<PrivacyElement>,
activityStarter: (String, Int) -> Unit
-) : SystemUIDialog(context, R.style.ScreenRecord) {
+) : SystemUIDialog(context, R.style.PrivacyDialog) {
private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
private val dismissed = AtomicBoolean(false)
@@ -64,6 +64,7 @@ class PrivacyDialog(
super.onCreate(savedInstanceState)
window?.apply {
attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
+ attributes.receiveInsetsIgnoringZOrder = true
setLayout(context.resources.getDimensionPixelSize(R.dimen.qs_panel_width), WRAP_CONTENT)
setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
new file mode 100644
index 000000000000..42d603ec5051
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsController.java
@@ -0,0 +1,140 @@
+/*
+ * 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.qs;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.ColorDisplayManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+
+/**
+ * @hide
+ */
+public class ReduceBrightColorsController implements
+ CallbackController<ReduceBrightColorsController.Listener> {
+ private final ColorDisplayManager mManager;
+ private final UserTracker mUserTracker;
+ private UserTracker.Callback mCurrentUserTrackerCallback;
+ private final Handler mHandler;
+ private final ContentObserver mContentObserver;
+ private final SecureSettings mSecureSettings;
+ private final ArrayList<ReduceBrightColorsController.Listener> mListeners = new ArrayList<>();
+
+ @Inject
+ public ReduceBrightColorsController(UserTracker userTracker,
+ @Background Handler handler,
+ ColorDisplayManager colorDisplayManager,
+ SecureSettings secureSettings) {
+ mManager = colorDisplayManager;
+ mUserTracker = userTracker;
+ mHandler = handler;
+ mSecureSettings = secureSettings;
+ mContentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ synchronized (mListeners) {
+ if (setting != null && mListeners.size() != 0) {
+ if (setting.equals(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
+ for (Listener listener : mListeners) {
+ listener.onActivated(mManager.isReduceBrightColorsActivated());
+ }
+ }
+ }
+ }
+ }
+ };
+
+ mCurrentUserTrackerCallback = new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, Context userContext) {
+ synchronized (mListeners) {
+ if (mListeners.size() > 0) {
+ mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
+ false, mContentObserver, newUser);
+ }
+ }
+ }
+ };
+ mUserTracker.addCallback(mCurrentUserTrackerCallback, new HandlerExecutor(handler));
+ }
+
+ @Override
+ public void addCallback(@NonNull Listener listener) {
+ synchronized (mListeners) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.getUriFor(
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
+ false, mContentObserver, mUserTracker.getUserId());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void removeCallback(@androidx.annotation.NonNull Listener listener) {
+ synchronized (mListeners) {
+ if (mListeners.remove(listener) && mListeners.size() == 0) {
+ mSecureSettings.unregisterContentObserver(mContentObserver);
+ }
+ }
+ }
+
+ /** Returns {@code true} if Reduce Bright Colors is activated */
+ public boolean isReduceBrightColorsActivated() {
+ return mManager.isReduceBrightColorsActivated();
+ }
+
+ /** Sets the activation state of Reduce Bright Colors */
+ public void setReduceBrightColorsActivated(boolean activated) {
+ mManager.setReduceBrightColorsActivated(activated);
+ }
+
+ /**
+ * Listener invoked whenever the Reduce Bright Colors settings are changed.
+ */
+ public interface Listener {
+ /**
+ * Listener invoked when the activated state changes.
+ *
+ * @param activated {@code true} if Reduce Bright Colors is activated.
+ */
+ default void onActivated(boolean activated) {
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index c3cc3af10e83..52f111e7ab48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,7 +18,6 @@ package com.android.systemui.qs
import android.content.Context
import android.util.AttributeSet
-import com.android.systemui.R
open class SideLabelTileLayout(
context: Context,
@@ -28,9 +27,6 @@ open class SideLabelTileLayout(
override fun updateResources(): Boolean {
return super.updateResources().also {
mMaxAllowedRows = 4
- mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt()
- mCellMarginVertical = mCellMarginHorizontal
- mMaxCellHeight = context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 47cb45b14b9b..ce8f6c1737d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -16,19 +16,19 @@ package com.android.systemui.qs.customize;
import android.content.Context;
import android.view.View;
-import android.widget.TextView;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.tileimpl.QSTileView;
-public class CustomizeTileView extends QSTileView {
+public class CustomizeTileView extends QSTileView implements TileAdapter.CustomizeView {
private boolean mShowAppLabel;
public CustomizeTileView(Context context, QSIconView icon) {
super(context, icon);
}
+ @Override
public void setShowAppLabel(boolean showAppLabel) {
mShowAppLabel = showAppLabel;
mSecondLine.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
@@ -41,10 +41,6 @@ public class CustomizeTileView extends QSTileView {
mSecondLine.setVisibility(mShowAppLabel ? View.VISIBLE : View.GONE);
}
- public TextView getAppLabel() {
- return mSecondLine;
- }
-
@Override
protected boolean animationsEnabled() {
return false;
@@ -54,4 +50,9 @@ public class CustomizeTileView extends QSTileView {
public boolean isLongClickable() {
return false;
}
+
+ @Override
+ public void changeState(QSTile.State state) {
+ handleStateChanged(state);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
new file mode 100644
index 000000000000..4ffcd8cdd9ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.qs.customize
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.view.View
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileViewHorizontal
+
+/**
+ * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
+ *
+ * This is a class parallel to [CustomizeTileView], but inheriting from [QSTileViewHorizontal].
+ */
+class CustomizeTileViewHorizontal(
+ context: Context,
+ icon: QSIconView
+) : QSTileViewHorizontal(context, icon),
+ TileAdapter.CustomizeView {
+
+ private var showAppLabel = false
+
+ override fun setShowAppLabel(showAppLabel: Boolean) {
+ this.showAppLabel = showAppLabel
+ mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
+ mLabel.isSingleLine = showAppLabel
+ }
+
+ override fun handleStateChanged(state: QSTile.State) {
+ super.handleStateChanged(state)
+ mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
+ }
+
+ override fun animationsEnabled(): Boolean {
+ return false
+ }
+
+ override fun isLongClickable(): Boolean {
+ return false
+ }
+
+ override fun changeState(state: QSTile.State) {
+ handleStateChanged(state)
+ }
+
+ override fun newTileBackground(): Drawable? {
+ super.newTileBackground()
+ return paintDrawable
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 7a91421b00a1..0adc8448b89f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs.customize;
+import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -30,6 +32,7 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
@@ -41,6 +44,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -49,11 +53,13 @@ import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
+import com.android.systemui.qs.tileimpl.QSTileView;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Named;
/** */
@QSScope
@@ -75,7 +81,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private static final int ACTION_ADD = 1;
private static final int ACTION_MOVE = 2;
- private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns;
+ private static final int NUM_COLUMNS_ID = R.integer.quick_settings_num_columns;
private final Context mContext;
@@ -102,9 +108,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private final AccessibilityDelegateCompat mAccessibilityDelegate;
private RecyclerView mRecyclerView;
private int mNumColumns;
+ private final boolean mUseHorizontalTiles;
@Inject
- public TileAdapter(Context context, QSTileHost qsHost, UiEventLogger uiEventLogger) {
+ public TileAdapter(Context context, QSTileHost qsHost, UiEventLogger uiEventLogger,
+ @Named(QS_LABELS_FLAG) boolean useHorizontalTiles) {
mContext = context;
mHost = qsHost;
mUiEventLogger = uiEventLogger;
@@ -114,6 +122,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID);
mAccessibilityDelegate = new TileAdapterDelegate();
+ mUseHorizontalTiles = useHorizontalTiles;
}
@Override
@@ -271,7 +280,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- frame.addView(new CustomizeTileView(context, new QSIconViewImpl(context)));
+ View view = mUseHorizontalTiles
+ ? new CustomizeTileViewHorizontal(context, new QSIconViewImpl(context))
+ : new CustomizeTileView(context, new QSIconViewImpl(context));
+ frame.addView(view);
return new Holder(frame);
}
@@ -354,8 +366,9 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
info.state.expandedAccessibilityClassName = "";
- holder.mTileView.handleStateChanged(info.state);
- holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
+ // The holder has a tileView, therefore this call is not null
+ holder.getTileAsCustomizeView().changeState(info.state);
+ holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
holder.mTileView.setClickable(true);
holder.mTileView.setOnClickListener(null);
@@ -534,25 +547,34 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
}
public class Holder extends ViewHolder {
- private CustomizeTileView mTileView;
+ private QSTileView mTileView;
public Holder(View itemView) {
super(itemView);
if (itemView instanceof FrameLayout) {
- mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
- mTileView.setBackground(null);
+ mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
+ if (mTileView instanceof CustomizeTileView) {
+ mTileView.setBackground(null);
+ }
mTileView.getIcon().disableAnimation();
mTileView.setTag(this);
ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
}
}
+ @Nullable
+ public CustomizeView getTileAsCustomizeView() {
+ return (CustomizeView) mTileView;
+ }
+
public void clearDrag() {
itemView.clearAnimation();
- mTileView.findViewById(R.id.tile_label).clearAnimation();
- mTileView.findViewById(R.id.tile_label).setAlpha(1);
- mTileView.getAppLabel().clearAnimation();
- mTileView.getAppLabel().setAlpha(.6f);
+ if (mTileView instanceof CustomizeTileView) {
+ mTileView.findViewById(R.id.tile_label).clearAnimation();
+ mTileView.findViewById(R.id.tile_label).setAlpha(1);
+ mTileView.getAppLabel().clearAnimation();
+ mTileView.getAppLabel().setAlpha(.6f);
+ }
}
public void startDrag() {
@@ -560,12 +582,14 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
.setDuration(DRAG_LENGTH)
.scaleX(DRAG_SCALE)
.scaleY(DRAG_SCALE);
- mTileView.findViewById(R.id.tile_label).animate()
- .setDuration(DRAG_LENGTH)
- .alpha(0);
- mTileView.getAppLabel().animate()
- .setDuration(DRAG_LENGTH)
- .alpha(0);
+ if (mTileView instanceof CustomizeTileView) {
+ mTileView.findViewById(R.id.tile_label).animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(0);
+ mTileView.getAppLabel().animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(0);
+ }
}
public void stopDrag() {
@@ -573,12 +597,14 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
.setDuration(DRAG_LENGTH)
.scaleX(1)
.scaleY(1);
- mTileView.findViewById(R.id.tile_label).animate()
- .setDuration(DRAG_LENGTH)
- .alpha(1);
- mTileView.getAppLabel().animate()
- .setDuration(DRAG_LENGTH)
- .alpha(.6f);
+ if (mTileView instanceof CustomizeTileView) {
+ mTileView.findViewById(R.id.tile_label).animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(1);
+ mTileView.getAppLabel().animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(.6f);
+ }
}
boolean canRemove() {
@@ -722,7 +748,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
int position = mCurrentDrag.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
TileInfo info = mTiles.get(position);
- mCurrentDrag.mTileView.setShowAppLabel(
+ ((CustomizeView) mCurrentDrag.mTileView).setShowAppLabel(
position > mEditIndex && !info.isSystem);
mCurrentDrag.stopDrag();
mCurrentDrag = null;
@@ -782,4 +808,9 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
public void onSwiped(ViewHolder viewHolder, int direction) {
}
};
+
+ interface CustomizeView {
+ void setShowAppLabel(boolean showAppLabel);
+ void changeState(@NonNull QSTile.State state);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 33713f3724c7..d41bd7ad4d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.dagger;
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
import android.content.Context;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
@@ -25,6 +27,7 @@ import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -32,6 +35,8 @@ import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.util.settings.SecureSettings;
+import javax.inject.Named;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -54,7 +59,9 @@ public interface QSModule {
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
NightDisplayListener nightDisplayListener,
- CastController castController) {
+ CastController castController,
+ ReduceBrightColorsController reduceBrightColorsController,
+ @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
AutoTileManager manager = new AutoTileManager(
context,
autoAddTrackerBuilder,
@@ -65,7 +72,9 @@ public interface QSModule {
dataSaverController,
managedProfileController,
nightDisplayListener,
- castController
+ castController,
+ reduceBrightColorsController,
+ isReduceBrightColorsAvailable
);
manager.init();
return manager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 38e2ba4df79a..33ca7d6bafd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -259,23 +259,30 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
final StringBuilder stateDescription = new StringBuilder();
+ String text = "";
switch (state.state) {
case Tile.STATE_UNAVAILABLE:
- stateDescription.append(mContext.getString(R.string.tile_unavailable));
+ text = mContext.getString(R.string.tile_unavailable);
break;
case Tile.STATE_INACTIVE:
if (state instanceof QSTile.BooleanState) {
- stateDescription.append(mContext.getString(R.string.switch_bar_off));
+ text = mContext.getString(R.string.switch_bar_off);
}
break;
case Tile.STATE_ACTIVE:
if (state instanceof QSTile.BooleanState) {
- stateDescription.append(mContext.getString(R.string.switch_bar_on));
+ text = mContext.getString(R.string.switch_bar_on);
}
break;
default:
break;
}
+ if (!TextUtils.isEmpty(text)) {
+ stateDescription.append(text);
+ if (TextUtils.isEmpty(state.secondaryLabel)) {
+ state.secondaryLabel = text;
+ }
+ }
if (!TextUtils.isEmpty(state.stateDescription)) {
stateDescription.append(", ");
stateDescription.append(state.stateDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 207b25d001b9..b59326ae56d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -174,4 +174,8 @@ public class QSTileView extends QSTileBaseView {
mLabelContainer.setClickable(false);
mLabelContainer.setLongClickable(false);
}
+
+ public TextView getAppLabel() {
+ return mSecondLine;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 07d48f32ff20..231037fdd158 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -35,13 +35,13 @@ import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
// Placeholder
private const val CORNER_RADIUS = 40f
-class QSTileViewHorizontal(
+open class QSTileViewHorizontal(
context: Context,
icon: QSIconView
) : QSTileView(context, icon, false) {
- private var paintDrawable: PaintDrawable? = null
- private var paintColor = Color.TRANSPARENT
+ protected var paintDrawable: PaintDrawable? = null
+ private var paintColor = Color.WHITE
private var paintAnimator: ValueAnimator? = null
init {
@@ -103,7 +103,7 @@ class QSTileViewHorizontal(
mSecondLine.setTextColor(mLabel.textColors)
mLabelContainer.background = null
- val allowAnimations = animationsEnabled() && paintColor != Color.TRANSPARENT
+ val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
val newColor = getCircleColor(state.state)
if (allowAnimations) {
animateToNewState(newColor)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index f94cabcee297..aec7b9a4b6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -33,46 +33,39 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
import javax.inject.Named;
/** Quick settings tile: Reduce Bright Colors **/
-public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
+public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
+ implements ReduceBrightColorsController.Listener{
//TODO(b/170973645): get icon drawable
private final Icon mIcon = null;
- private final SecureSetting mActivatedSetting;
private final boolean mIsAvailable;
+ private final ReduceBrightColorsController mReduceBrightColorsController;
+ private boolean mIsListening;
@Inject
public ReduceBrightColorsTile(
@Named(RBC_AVAILABLE) boolean isAvailable,
+ ReduceBrightColorsController reduceBrightColorsController,
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
- QSLogger qsLogger,
- UserTracker userTracker,
- SecureSettings secureSettings
+ QSLogger qsLogger
) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
activityStarter, qsLogger);
-
- mActivatedSetting = new SecureSetting(secureSettings, mainHandler,
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, userTracker.getUserId()) {
- @Override
- protected void handleValueChanged(int value, boolean observedChange) {
- refreshState();
- }
- };
+ mReduceBrightColorsController = reduceBrightColorsController;
+ mReduceBrightColorsController.observe(getLifecycle(), this);
mIsAvailable = isAvailable;
}
@@ -84,7 +77,6 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@Override
protected void handleDestroy() {
super.handleDestroy();
- mActivatedSetting.setListening(false);
}
@Override
@@ -93,25 +85,13 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
}
@Override
- public void handleSetListening(boolean listening) {
- super.handleSetListening(listening);
- mActivatedSetting.setListening(listening);
- }
-
- @Override
- protected void handleUserSwitch(int newUserId) {
- mActivatedSetting.setUserId(newUserId);
- refreshState();
- }
-
- @Override
public Intent getLongClickIntent() {
return new Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS);
}
@Override
protected void handleClick() {
- mActivatedSetting.setValue(mState.value ? 0 : 1);
+ mReduceBrightColorsController.setReduceBrightColorsActivated(!mState.value);
}
@Override
@@ -121,7 +101,7 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.value = mActivatedSetting.getValue() == 1;
+ state.value = mReduceBrightColorsController.isReduceBrightColorsActivated();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label);
state.expandedAccessibilityClassName = Switch.class.getName();
@@ -132,4 +112,9 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
public int getMetricsCategory() {
return 0;
}
+
+ @Override
+ public void onActivated(boolean activated) {
+ refreshState();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5b2a7e7ff617..e7d42832878b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -662,14 +662,29 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+ public void startIntent(PendingIntent intent, Intent fillInIntent,
+ int stage, int position, Bundle options) {
if (!verifyCaller("startIntent")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s ->
- s.startIntent(intent, stage, position, options));
+ s.startIntent(intent, mContext, fillInIntent, stage, position, options));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeFromSideStage(int taskId) {
+ if (!verifyCaller("removeFromSideStage")) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSplitScreenOptional.ifPresent(
+ s -> s.removeFromSideStage(taskId));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -789,10 +804,10 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void onTaskStageChanged(int taskId, int stage) {
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
try {
if (mISplitScreenListener != null) {
- mISplitScreenListener.onTaskStageChanged(taskId, stage);
+ mISplitScreenListener.onTaskStageChanged(taskId, stage, visible);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "onTaskStageChanged", e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 197582104f8e..bd46ffec759b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -351,7 +351,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
- .setContentTitle(getResources().getString(R.string.screenrecord_save_message))
+ .setContentTitle(getResources().getString(R.string.screenrecord_save_title))
+ .setContentText(getResources().getString(R.string.screenrecord_save_text))
.setContentIntent(PendingIntent.getActivity(
this,
REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 5b55864eed8a..c066619d049a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -65,6 +65,9 @@ public class CropView extends View {
private float mTopDelta = 0f;
private float mBottomDelta = 0f;
+ private int mExtraTopPadding;
+ private int mExtraBottomPadding;
+
private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
private float mStartingY; // y coordinate of ACTION_DOWN
private CropInteractionListener mCropInteractionListener;
@@ -117,12 +120,13 @@ public class CropView extends View {
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
float delta = event.getY() - mStartingY;
if (mCurrentDraggingBoundary == CropBoundary.TOP) {
- mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx,
+ mTopDelta = pixelDistanceToFraction((int) MathUtils.constrain(delta,
+ -topPx + mExtraTopPadding,
bottomPx - 2 * mCropTouchMargin - topPx));
} else { // Bottom
- mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
+ mBottomDelta = pixelDistanceToFraction((int) MathUtils.constrain(delta,
topPx + 2 * mCropTouchMargin - bottomPx,
- getHeight() - bottomPx));
+ getHeight() - bottomPx - mExtraBottomPadding));
}
updateListener(event);
invalidate();
@@ -195,6 +199,16 @@ public class CropView extends View {
}
/**
+ * Set additional top and bottom padding for the image being cropped (used when the
+ * corresponding ImageView doesn't take the full height).
+ */
+ public void setExtraPadding(int top, int bottom) {
+ mExtraTopPadding = top;
+ mExtraBottomPadding = bottom;
+ invalidate();
+ }
+
+ /**
* @return value [0,1] representing the position of the top crop boundary. Does not reflect
* changes from any in-progress touch input.
*/
@@ -244,12 +258,22 @@ public class CropView extends View {
true, mHandlePaint);
}
+ /**
+ * Convert the given fraction position to pixel position within the View.
+ */
private int fractionToPixels(float frac) {
- return (int) (frac * getHeight());
+ return (int) (mExtraTopPadding + frac * getImageHeight());
+ }
+
+ private int getImageHeight() {
+ return getHeight() - mExtraTopPadding - mExtraBottomPadding;
}
- private float pixelsToFraction(int px) {
- return px / (float) getHeight();
+ /**
+ * Convert the given pixel distance to fraction of the image.
+ */
+ private float pixelDistanceToFraction(int px) {
+ return px / (float) getImageHeight();
}
private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 5a13ea55222d..4dc846e0e95d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -157,6 +157,9 @@ public class LongScreenshotActivity extends Activity {
});
}
}
+ mPreview.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ updateCropLocation());
}
@Override
@@ -305,6 +308,27 @@ public class LongScreenshotActivity extends Activity {
}
}
+ private void updateCropLocation() {
+ Drawable drawable = mPreview.getDrawable();
+ if (drawable == null) {
+ return;
+ }
+
+ float imageRatio = drawable.getBounds().width() / (float) drawable.getBounds().height();
+ float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight();
+
+ if (imageRatio > viewRatio) {
+ // Image is full width and height is constrained, compute extra padding to inform
+ // CropView
+ float imageHeight = mPreview.getHeight() * viewRatio / imageRatio;
+ int extraPadding = (int) (mPreview.getHeight() - imageHeight) / 2;
+ mCropView.setExtraPadding(extraPadding, extraPadding);
+ } else {
+ // Image is full height
+ mCropView.setExtraPadding(0, 0);
+ }
+ }
+
private void doCapture() {
mScrollCaptureController.start(mConnection,
new ScrollCaptureController.ScrollCaptureCallback() {
@@ -319,6 +343,7 @@ public class LongScreenshotActivity extends Activity {
Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x "
+ imageTileSet.getHeight());
mPreview.setImageDrawable(imageTileSet.getDrawable());
+ updateCropLocation();
mMagnifierView.setDrawable(imageTileSet.getDrawable(),
imageTileSet.getWidth(), imageTileSet.getHeight());
mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
index 7a0ec4c520b2..90f304262ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenshot;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
@@ -28,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import androidx.annotation.Nullable;
@@ -35,7 +38,7 @@ import com.android.systemui.R;
/**
* MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and
- * positioning dereived from events from a CropView to which it listens.
+ * positioning derived from events from a CropView to which it listens.
*
* Not meant to be a general-purpose magnifier!
*/
@@ -57,6 +60,20 @@ public class MagnifierView extends View implements CropView.CropInteractionListe
private float mLastCropPosition;
private CropView.CropBoundary mCropBoundary;
+ private ViewPropertyAnimator mTranslationAnimator;
+ private final Animator.AnimatorListener mTranslationAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mTranslationAnimator = null;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTranslationAnimator = null;
+ }
+ };
+
public MagnifierView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -133,6 +150,8 @@ public class MagnifierView extends View implements CropView.CropInteractionListe
public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
float cropPosition, int cropPositionPx) {
mCropBoundary = boundary;
+ boolean touchOnRight = event.getX() > getParentWidth() / 2;
+ float translateXTarget = touchOnRight ? 0 : getParentWidth() - getWidth();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastCropPosition = cropPosition;
@@ -144,11 +163,22 @@ public class MagnifierView extends View implements CropView.CropInteractionListe
setAlpha(0f);
setTranslationX((getParentWidth() - getWidth()) / 2);
setVisibility(View.VISIBLE);
- boolean touchOnRight = event.getX() > getParentWidth() / 2;
- float translateXTarget = touchOnRight ? 0 : getParentWidth() - getWidth();
- animate().alpha(1f).translationX(translateXTarget).scaleX(1f).scaleY(1f).start();
+ mTranslationAnimator =
+ animate().alpha(1f).translationX(translateXTarget).scaleX(1f).scaleY(1f);
+ mTranslationAnimator.setListener(mTranslationAnimatorListener);
+ mTranslationAnimator.start();
break;
case MotionEvent.ACTION_MOVE:
+ // The touch is near the middle if it's within 10% of the center point.
+ // We don't want to animate horizontally if the touch is near the middle.
+ boolean nearMiddle = Math.abs(event.getX() - getParentWidth() / 2)
+ < getParentWidth() / 10f;
+ boolean viewOnLeft = getTranslationX() < (getParentWidth() - getWidth()) / 2;
+ if (!nearMiddle && viewOnLeft != touchOnRight && mTranslationAnimator == null) {
+ mTranslationAnimator = animate().translationX(translateXTarget);
+ mTranslationAnimator.setListener(mTranslationAnimatorListener);
+ mTranslationAnimator.start();
+ }
mLastCropPosition = cropPosition;
setTranslationY(cropPositionPx - getHeight() / 2);
invalidate();
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/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d2ddd212bda6..5e8245f8e0e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -527,7 +527,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
handleCustomTransformHeight(view, expandingAnimated, iconState);
float fullTransitionAmount;
- float transitionAmount;
+ float iconTransitionAmount;
float contentTransformationAmount;
float shelfStart = getTranslationY();
boolean fullyInOrOut = true;
@@ -549,18 +549,19 @@ public class NotificationShelf extends ActivatableNotificationView implements
fullTransitionAmount = 1.0f - interpolatedAmount;
if (isLastChild) {
- // If it's the last child we should use all of the notification to transform
- // instead of just to the icon, since that can be quite low.
- transitionAmount = (shelfStart - viewStart) / transformDistance;
+ // Reduce icon transform distance to completely fade in shelf icon
+ // by the time the notification icon fades out, and vice versa
+ iconTransitionAmount = (shelfStart - viewStart)
+ / (iconTransformStart - viewStart);
} else {
- transitionAmount = (shelfStart - iconTransformStart) / transformDistance;
+ iconTransitionAmount = (shelfStart - iconTransformStart) / transformDistance;
}
- transitionAmount = MathUtils.constrain(transitionAmount, 0.0f, 1.0f);
- transitionAmount = 1.0f - transitionAmount;
+ iconTransitionAmount = MathUtils.constrain(iconTransitionAmount, 0.0f, 1.0f);
+ iconTransitionAmount = 1.0f - iconTransitionAmount;
fullyInOrOut = false;
} else {
fullTransitionAmount = 1.0f;
- transitionAmount = 1.0f;
+ iconTransitionAmount = 1.0f;
}
// Transforming the content
@@ -569,7 +570,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
contentTransformationAmount = 1.0f - contentTransformationAmount;
} else {
fullTransitionAmount = 0.0f;
- transitionAmount = 0.0f;
+ iconTransitionAmount = 0.0f;
contentTransformationAmount = 0.0f;
}
if (iconState != null && fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
@@ -585,7 +586,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
view.setContentTransformationAmount(contentTransformationAmount, isLastChild);
// Update the positioning of the icon
- updateIconPositioning(view, transitionAmount, fullTransitionAmount,
+ updateIconPositioning(view, iconTransitionAmount, fullTransitionAmount,
transformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
return fullTransitionAmount;
@@ -679,8 +680,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
|| iconState.useLinearTransitionAmount) {
transitionAmount = iconTransitionAmount;
} else {
- // We take the clamped position instead
- transitionAmount = clampedAmount;
+ transitionAmount = iconTransitionAmount;
iconState.needsCannedAnimation = iconState.clampedAppearAmount != clampedAmount
&& !mNoAnimationsInThisFrame;
}
@@ -689,8 +689,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
? fullTransitionAmount
: transitionAmount;
iconState.clampedAppearAmount = clampedAmount;
- setIconTransformationAmount(view, transitionAmount, iconTransformDistance,
- clampedAmount != transitionAmount, isLastChild);
+ setIconTransformationAmount(view, transitionAmount, isLastChild);
}
private boolean isTargetClipped(ExpandableView view) {
@@ -708,7 +707,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private void setIconTransformationAmount(ExpandableView view,
- float transitionAmount, float iconTransformDistance, boolean usingLinearInterpolation,
+ float transitionAmount,
boolean isLastChild) {
if (!(view instanceof ExpandableNotificationRow)) {
return;
@@ -720,42 +719,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
View rowIcon = row.getShelfTransformationTarget();
// Let's resolve the relative positions of the icons
- float notificationIconSize = 0.0f;
- int iconTopPadding;
int iconStartPadding;
if (rowIcon != null) {
- iconTopPadding = row.getRelativeTopPadding(rowIcon);
iconStartPadding = row.getRelativeStartPadding(rowIcon);
- notificationIconSize = rowIcon.getHeight();
} else {
- iconTopPadding = mIconAppearTopPadding;
iconStartPadding = 0;
}
-
- float shelfIconSize = mAmbientState.isFullyHidden() ? mHiddenShelfIconSize : mIconSize;
- shelfIconSize = shelfIconSize * icon.getIconScale();
-
- // Get the icon correctly positioned in Y
- float notificationIconPositionY = row.getTranslationY() + row.getContentTranslation();
- float targetYPosition = 0;
boolean stayingInShelf = row.isInShelf() && !row.isTransformingIntoShelf();
- if (usingLinearInterpolation && !stayingInShelf) {
- // If we interpolate from the notification position, this might lead to a slightly
- // odd interpolation, since the notification position changes as well.
- // Let's instead interpolate directly to the top left of the notification
- targetYPosition = NotificationUtils.interpolate(
- Math.min(notificationIconPositionY + mIconAppearTopPadding
- - getTranslationY(), 0),
- 0,
- transitionAmount);
- }
- notificationIconPositionY += iconTopPadding;
- float shelfIconPositionY = getTranslationY() + icon.getTop();
- shelfIconPositionY += (icon.getHeight() - shelfIconSize) / 2.0f;
- float iconYTranslation = NotificationUtils.interpolate(
- notificationIconPositionY - shelfIconPositionY,
- targetYPosition,
- transitionAmount);
// Get the icon correctly positioned in X
// Even in RTL it's the left, since we're inverting the location in post
@@ -767,28 +737,19 @@ public class NotificationShelf extends ActivatableNotificationView implements
transitionAmount);
// Let's handle the case that there's no Icon
- float alpha = 1.0f;
boolean noIcon = !row.isShowingIcon();
if (noIcon) {
// The view currently doesn't have an icon, lets transform it in!
- alpha = transitionAmount;
- notificationIconSize = shelfIconSize / 2.0f;
iconXTranslation = mShelfIcons.getActualPaddingStart();
}
- // The notification size is different from the size in the shelf / statusbar
- float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
- transitionAmount);
if (iconState != null) {
- iconState.scaleX = newSize / shelfIconSize;
- iconState.scaleY = iconState.scaleX;
iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
if (isAppearing) {
iconState.hidden = true;
iconState.iconAppearAmount = 0.0f;
}
- iconState.alpha = alpha;
- iconState.yTranslation = iconYTranslation;
+ iconState.alpha = transitionAmount;
iconState.xTranslation = iconXTranslation;
if (stayingInShelf) {
iconState.iconAppearAmount = 1.0f;
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/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index d1ab7ea55d57..004cf9968a77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -180,9 +180,6 @@ class ConversationNotificationManager @Inject constructor(
}
if (changed) {
notificationGroupManager.updateIsolation(entry)
- // ensure that the conversation icon isn't hidden
- // (ex: if it was showing in the shelf)
- entry.row?.updateIconVisibilities()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 8a22b9f6891f..dbd8580b751e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -176,7 +176,6 @@ public final class NotificationEntry extends ListEntry {
private int mBucket = BUCKET_ALERTING;
@Nullable private Long mPendingAnimationDuration;
private boolean mIsMarkedForUserTriggeredMovement;
- private boolean mShelfIconVisible;
private boolean mIsAlerting;
public boolean mRemoteEditImeVisible;
@@ -417,7 +416,6 @@ public final class NotificationEntry extends ListEntry {
//TODO: This will go away when we have a way to bind an entry to a row
public void setRow(ExpandableNotificationRow row) {
this.row = row;
- updateShelfIconVisibility();
}
public ExpandableNotificationRowController getRowController() {
@@ -938,19 +936,6 @@ public final class NotificationEntry extends ListEntry {
return mIsMarkedForUserTriggeredMovement;
}
- /** Whether or not the icon for this notification is visible in the shelf. */
- public void setShelfIconVisible(boolean shelfIconVisible) {
- if (row == null) return;
- mShelfIconVisible = shelfIconVisible;
- updateShelfIconVisibility();
- }
-
- private void updateShelfIconVisibility() {
- if (row != null) {
- row.setShelfIconVisible(mShelfIconVisible);
- }
- }
-
/**
* Mark this entry for movement triggered by a user action (ex: changing the priorirty of a
* conversation). This can then be used for custom animations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index ba45f9a687ed..5375ac345e50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -95,11 +95,6 @@ class IconManager @Inject constructor(
// Construct the shelf icon view.
val shelfIcon = iconBuilder.createIconView(entry)
shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
-
- // TODO: This doesn't belong here
- shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
- entry.setShelfIconVisible(newVisibility == View.VISIBLE)
- }
shelfIcon.visibility = View.INVISIBLE
// Construct the aod icon view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 0ad6507fb01e..dff97a679164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.init
+import android.content.Context
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
@@ -58,6 +60,7 @@ import javax.inject.Inject
*/
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
+ private val context: Context,
private val featureFlags: FeatureFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
@@ -129,7 +132,9 @@ class NotificationsControllerImpl @Inject constructor(
entryManager.attach(notificationListener)
}
- if (featureFlags.isPeopleTileEnabled) {
+ val showPeopleSpace = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.SHOW_PEOPLE_SPACE, 1)
+ if (showPeopleSpace == 1) {
peopleSpaceWidgetManager.attach(notificationListener)
}
}
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..0f23b770aacd 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
@@ -331,7 +331,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mHeadsupDisappearRunning;
private View mChildAfterViewWhenDismissed;
private View mGroupParentWhenDismissed;
- private boolean mShelfIconVisible;
private boolean mAboveShelf;
private OnUserInteractionCallback mOnUserInteractionCallback;
private NotificationGutsManager mNotificationGutsManager;
@@ -568,7 +567,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// The public layouts expand button is always visible
mPublicLayout.updateExpandButtons(true);
updateLimits();
- updateIconVisibilities();
updateShelfIconColor();
updateRippleAllowed();
if (mUpdateBackgroundOnUpdate) {
@@ -883,7 +881,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
setDistanceToTopRoundness(NO_ROUNDNESS);
mNotificationParent.updateBackgroundForGroupState();
}
- updateIconVisibilities();
updateBackgroundClipping();
}
@@ -1481,21 +1478,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return getShelfTransformationTarget() != null;
}
- /**
- * Set the icons to be visible of this notification.
- */
- public void setShelfIconVisible(boolean iconVisible) {
- if (iconVisible != mShelfIconVisible) {
- mShelfIconVisible = iconVisible;
- updateIconVisibilities();
- }
- }
-
- @Override
- protected void onBelowSpeedBumpChanged() {
- updateIconVisibilities();
- }
-
@Override
protected void updateContentTransformation() {
if (mExpandAnimationRunning) {
@@ -1522,18 +1504,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
- /** Refreshes the visibility of notification icons */
- public void updateIconVisibilities() {
- // The shelf icon is never hidden for children in groups
- boolean visible = !isChildInGroup() && mShelfIconVisible;
- for (NotificationContentView l : mLayouts) {
- l.setShelfIconVisible(visible);
- }
- if (mChildrenContainer != null) {
- mChildrenContainer.setShelfIconVisible(visible);
- }
- }
-
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
mPrivateLayout.setIsLowPriority(isLowPriority);
@@ -2052,8 +2022,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..88cf2db527f0 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;
@@ -169,7 +172,6 @@ public class NotificationContentView extends FrameLayout {
private int mContentHeightAtAnimationStart = UNDEFINED;
private boolean mFocusOnVisibilityChange;
private boolean mHeadsUpAnimatingAway;
- private boolean mShelfIconVisible;
private int mClipBottomAmount;
private boolean mIsLowPriority;
private boolean mIsContentExpandable;
@@ -1234,6 +1236,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 +1274,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 +1354,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 +1391,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)
@@ -1729,23 +1738,6 @@ public class NotificationContentView extends FrameLayout {
mFocusOnVisibilityChange = true;
}
- public void setShelfIconVisible(boolean iconsVisible) {
- mShelfIconVisible = iconsVisible;
- updateIconVisibilities();
- }
-
- private void updateIconVisibilities() {
- if (mContractedWrapper != null) {
- mContractedWrapper.setShelfIconVisible(mShelfIconVisible);
- }
- if (mHeadsUpWrapper != null) {
- mHeadsUpWrapper.setShelfIconVisible(mShelfIconVisible);
- }
- if (mExpandedWrapper != null) {
- mExpandedWrapper.setShelfIconVisible(mShelfIconVisible);
- }
- }
-
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 7248bcef621c..151840afcef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -44,9 +44,10 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl
private void updateImageTag(StatusBarNotification notification) {
final Bundle extras = notification.getNotification().extras;
- Icon overRiddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
- if (overRiddenIcon != null) {
- mPicture.setTag(ImageTransformState.ICON_TAG, overRiddenIcon);
+ Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG);
+ if (overriddenIcon != null) {
+ mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 905bccfa6cdf..fb0fdcccd4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -132,20 +132,6 @@ class NotificationConversationTemplateViewWrapper constructor(
)
}
- override fun setShelfIconVisible(visible: Boolean) {
- if (conversationLayout.isImportantConversation) {
- if (conversationIconView.visibility != View.GONE) {
- conversationIconView.isForceHidden = visible
- // We don't want the small icon to be hidden by the extended wrapper, as force
- // hiding the conversationIcon will already do that via its listener.
- return
- }
- } else {
- conversationIconView.isForceHidden = false
- }
- super.setShelfIconVisible(visible)
- }
-
override fun getShelfTransformationTarget(): View? =
if (conversationLayout.isImportantConversation)
if (conversationIconView.visibility != View.GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index eb79e3c8a69a..bdafd232167d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -313,12 +313,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
- public void setShelfIconVisible(boolean visible) {
- super.setShelfIconVisible(visible);
- mIcon.setForceHidden(visible);
- }
-
- @Override
public TransformState getCurrentState(int fadingView) {
return mTransformationHelper.getCurrentState(fadingView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index e9934c0053b0..e0b58125aabd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -51,7 +51,8 @@ import com.android.systemui.statusbar.notification.row.HybridNotificationView;
public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
private final int mFullHeaderTranslation;
- protected ImageView mPicture;
+ protected ImageView mRightIcon;
+ protected ImageView mLeftIcon;
private ProgressBar mProgressBar;
private TextView mTitle;
private TextView mText;
@@ -140,9 +141,14 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
}
private void resolveTemplateViews(StatusBarNotification notification) {
- mPicture = mView.findViewById(com.android.internal.R.id.right_icon);
- if (mPicture != null) {
- mPicture.setTag(ImageTransformState.ICON_TAG,
+ mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
+ if (mRightIcon != null) {
+ mRightIcon.setTag(ImageTransformState.ICON_TAG,
+ notification.getNotification().getLargeIcon());
+ }
+ mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
+ if (mLeftIcon != null) {
+ mLeftIcon.setTag(ImageTransformState.ICON_TAG,
notification.getNotification().getLargeIcon());
}
mTitle = mView.findViewById(com.android.internal.R.id.title);
@@ -240,9 +246,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
resolveTemplateViews(row.getEntry().getSbn());
super.onContentUpdated(row);
// With the modern templates, a large icon visually overlaps the header, so we can't
- // simply hide the header -- just show the
+ // hide the header, we must show it.
mCanHideHeader = mNotificationHeader != null
- && (mPicture == null || mPicture.getVisibility() != VISIBLE);
+ && (mRightIcon == null || mRightIcon.getVisibility() != VISIBLE);
if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) {
setHeaderVisibleAmount(row.getHeaderVisibleAmount());
}
@@ -260,14 +266,15 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
mText);
}
- if (mPicture != null) {
+ if (mRightIcon != null) {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
- mPicture);
+ mRightIcon);
}
if (mProgressBar != null) {
mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
mProgressBar);
}
+ addViewsTransformingToSimilar(mLeftIcon);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 89babf0835c6..9ced12d32d27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -331,11 +331,6 @@ public abstract class NotificationViewWrapper implements TransformableView {
return null;
}
- /**
- * Set the shelf icon to be visible and hide our own icons.
- */
- public void setShelfIconVisible(boolean shelfIconVisible) {}
-
public int getHeaderTranslation(boolean forceNoHeader) {
return 0;
}
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..d8ee102064e1 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) {
@@ -1225,21 +1259,6 @@ public class NotificationChildrenContainer extends ViewGroup {
return 0;
}
- public void setShelfIconVisible(boolean iconVisible) {
- if (mNotificationHeaderWrapper != null) {
- CachingIconView icon = mNotificationHeaderWrapper.getIcon();
- if (icon != null) {
- icon.setForceHidden(iconVisible);
- }
- }
- if (mNotificationHeaderWrapperLowPriority != null) {
- CachingIconView icon = mNotificationHeaderWrapperLowPriority.getIcon();
- if (icon != null) {
- icon.setForceHidden(iconVisible);
- }
- }
- }
-
public void setClipBottomAmount(int clipBottomAmount) {
mClipBottomAmount = clipBottomAmount;
updateChildrenClipping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index e40c262765ea..204dd9f5e58c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.ColorDisplayManager;
@@ -27,6 +29,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.policy.CastController;
@@ -41,6 +44,8 @@ import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.Objects;
+import javax.inject.Named;
+
/**
* Manages which tiles should be automatically added to QS.
*/
@@ -69,6 +74,8 @@ public class AutoTileManager implements UserAwareController {
private final ManagedProfileController mManagedProfileController;
private final NightDisplayListener mNightDisplayListener;
private final CastController mCastController;
+ private final ReduceBrightColorsController mReduceBrightColorsController;
+ private final boolean mIsReduceBrightColorsAvailable;
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
@@ -79,7 +86,9 @@ public class AutoTileManager implements UserAwareController {
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
NightDisplayListener nightDisplayListener,
- CastController castController) {
+ CastController castController,
+ ReduceBrightColorsController reduceBrightColorsController,
+ @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
mContext = context;
mHost = host;
mSecureSettings = secureSettings;
@@ -91,6 +100,8 @@ public class AutoTileManager implements UserAwareController {
mManagedProfileController = managedProfileController;
mNightDisplayListener = nightDisplayListener;
mCastController = castController;
+ mReduceBrightColorsController = reduceBrightColorsController;
+ mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
}
/**
@@ -124,9 +135,9 @@ public class AutoTileManager implements UserAwareController {
if (!mAutoTracker.isAdded(CAST)) {
mCastController.addCallback(mCastCallback);
}
-
- // TODO(b/170970675): Set a listener/controller and callback for Reduce Bright Colors
- // state changes. Call into ColorDisplayService to get availability/config status
+ if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
+ mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
+ }
int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) {
@@ -143,6 +154,9 @@ public class AutoTileManager implements UserAwareController {
if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
mNightDisplayListener.setCallback(null);
}
+ if (mIsReduceBrightColorsAvailable) {
+ mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
+ }
mCastController.removeCallback(mCastCallback);
int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) {
@@ -287,6 +301,24 @@ public class AutoTileManager implements UserAwareController {
};
@VisibleForTesting
+ final ReduceBrightColorsController.Listener mReduceBrightColorsCallback =
+ new ReduceBrightColorsController.Listener() {
+ @Override
+ public void onActivated(boolean activated) {
+ if (activated) {
+ addReduceBrightColorsTile();
+ }
+ }
+
+ private void addReduceBrightColorsTile() {
+ if (mAutoTracker.isAdded(BRIGHTNESS)) return;
+ mHost.addTile(BRIGHTNESS);
+ mAutoTracker.setTileAdded(BRIGHTNESS);
+ mHandler.post(() -> mReduceBrightColorsController.removeCallback(this));
+ }
+ };
+
+ @VisibleForTesting
final CastController.Callback mCastCallback = new CastController.Callback() {
@Override
public void onCastDevicesChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 986333ce5010..80109cb7a06c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
@@ -74,10 +73,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.controls.dagger.ControlsComponent;
-import com.android.systemui.controls.ui.ControlsDialog;
-import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -130,7 +125,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
- private ImageView mAltLeftButton;
+ private ImageView mWalletButton;
private ViewGroup mIndicationArea;
private TextView mIndicationText;
private TextView mIndicationTextBottom;
@@ -179,11 +174,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private int mBurnInXOffset;
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
-
- private ControlsDialog mControlsDialog;
- private ControlsComponent mControlsComponent;
private int mLockScreenMode;
- private BroadcastDispatcher mBroadcastDispatcher;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardBottomAreaView(Context context) {
@@ -251,7 +242,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
- mAltLeftButton = findViewById(R.id.alt_left_button);
+ mWalletButton = findViewById(R.id.wallet_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
@@ -351,10 +342,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
- lp = mAltLeftButton.getLayoutParams();
+ lp = mWalletButton.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
- mAltLeftButton.setLayoutParams(lp);
+ mWalletButton.setLayoutParams(lp);
}
private void updateRightAffordanceIcon() {
@@ -427,11 +418,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setContentDescription(state.contentDescription);
}
- private void updateControlsVisibility() {
- if (mDozing || mControlsComponent.getVisibility() != AVAILABLE) {
- mAltLeftButton.setVisibility(GONE);
+ private void updateWalletVisibility() {
+ if (mDozing) {
+ mWalletButton.setVisibility(GONE);
} else {
- mAltLeftButton.setVisibility(VISIBLE);
+ mWalletButton.setVisibility(VISIBLE);
}
}
@@ -699,8 +690,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public void startFinishDozeAnimation() {
long delay = 0;
- if (mAltLeftButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mAltLeftButton, delay);
+ if (mWalletButton.getVisibility() == View.VISIBLE) {
+ startFinishDozeAnimationElement(mWalletButton, delay);
}
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
@@ -774,14 +765,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
updateCameraVisibility();
updateLeftAffordanceIcon();
- updateControlsVisibility();
+ updateWalletVisibility();
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
- if (mControlsDialog != null) {
- mControlsDialog.dismiss();
- mControlsDialog = null;
- }
} else {
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
@@ -811,7 +798,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha);
- mAltLeftButton.setAlpha(alpha);
+ mWalletButton.setAlpha(alpha);
}
private class DefaultLeftButton implements IntentButton {
@@ -884,38 +871,18 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
return insets;
}
- /**
- * Show or hide controls, depending on the lock screen mode and controls
- * availability.
- */
- public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) {
- mControlsComponent = component;
- mBroadcastDispatcher = dispatcher;
- setupControls();
- }
-
- private void setupControls() {
+ private void setupWallet() {
boolean inNewLayout = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
boolean settingEnabled = Settings.Global.getInt(mContext.getContentResolver(),
"controls_lockscreen", 0) == 1;
- if (!inNewLayout || !settingEnabled || !mControlsComponent.isEnabled()) {
- mAltLeftButton.setVisibility(View.GONE);
+ if (!inNewLayout || !settingEnabled) {
+ mWalletButton.setVisibility(View.GONE);
return;
}
- mControlsComponent.getControlsListingController().get()
- .addCallback(list -> {
- if (!list.isEmpty()) {
- mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
- mAltLeftButton.setOnClickListener((v) -> {
- ControlsUiController ui = mControlsComponent
- .getControlsUiController().get();
- mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
- .show(ui);
- });
- }
- updateControlsVisibility();
- });
+ // TODO: add image
+ // mWalletButton.setImageDrawable(list.get(0).loadIcon());
+ updateWalletVisibility();
}
/**
@@ -923,6 +890,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
*/
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
- setupControls();
+ setupWallet();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 83c347b05012..ae14fa943a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -89,10 +89,8 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -245,7 +243,6 @@ public class NotificationPanelViewController extends PanelViewController {
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
mClockPositionAlgorithm.onLockScreenModeChanged(mode);
- mKeyguardBottomArea.onLockScreenModeChanged(mode);
}
@Override
@@ -304,7 +301,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
- private final ControlsComponent mControlsComponent;
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -520,7 +516,6 @@ public class NotificationPanelViewController extends PanelViewController {
private NotificationShelfController mNotificationShelfController;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
- private BroadcastDispatcher mBroadcastDispatcher;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
@@ -578,9 +573,7 @@ public class NotificationPanelViewController extends PanelViewController {
UserManager userManager,
MediaDataManager mediaDataManager,
AmbientState ambientState,
- FeatureFlags featureFlags,
- ControlsComponent controlsComponent,
- BroadcastDispatcher broadcastDispatcher) {
+ FeatureFlags featureFlags) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -623,7 +616,6 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimController = scrimController;
mUserManager = userManager;
mMediaDataManager = mediaDataManager;
- mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -662,7 +654,6 @@ public class NotificationPanelViewController extends PanelViewController {
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
- mBroadcastDispatcher = broadcastDispatcher;
mView.setBackgroundColor(Color.TRANSPARENT);
OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -972,7 +963,6 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mKeyguardBottomArea.setStatusBar(mStatusBar);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
- mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher);
}
private void updateMaxDisplayedNotifications(boolean recompute) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 55744f94f2b0..b6ed3e50ed7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -27,7 +27,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.annotation.Nullable;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemClock;
@@ -1243,7 +1242,10 @@ public abstract class PanelViewController {
mVelocityTracker.clear();
break;
}
- return false;
+
+ // Finally, if none of the above cases applies, ensure that touches do not get handled
+ // by the contents of a panel that is not showing (a bit of a hack to avoid b/178277858)
+ return (mView.getVisibility() != View.VISIBLE);
}
@Override
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..0807f8aa5607 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,41 @@ public class StatusBar extends SystemUI implements DemoMode,
mMainThreadHandler.post(runnable);
}
- public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
- return getDefaultActivityOptions(animationAdapter).toBundle();
+ /**
+ * 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) {
+ ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+ options.setLaunchDisplayId(displayId);
+ options.setCallerDisplayId(displayId);
+ return options.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 +4562,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/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 6c097bdb08d3..044f52fd689e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -383,8 +383,15 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
int qsTypeIcon = 0;
IconState qsIcon = null;
CharSequence description = null;
+ // Mobile icon will only be shown in the statusbar in 2 scenarios
+ // 1. Mobile is the default network, and it is validated
+ // 2. Mobile is the default network, it is not validated and there is no other
+ // non-Carrier WiFi networks available.
+ boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
+ || (mCurrentState.inetCondition == 0
+ && !mNetworkController.isNonCarrierWifiNetworkAvailable());
// Only send data sim callbacks to QS.
- if (mCurrentState.dataSim && mCurrentState.isDefault) {
+ if (mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons) {
qsTypeIcon =
(showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
qsIcon = new IconState(mCurrentState.enabled
@@ -397,7 +404,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
boolean activityOut = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
- showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
+ showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons;
boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
showDataIcon |= mCurrentState.roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 8eb1e6487046..fbdaf9cdae20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -547,6 +547,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
return mWifiSignalController.isCarrierMergedWifi(subId);
}
+ boolean isNonCarrierWifiNetworkAvailable() {
+ return !mNoNetworksAvailable;
+ }
+
boolean isEthernetDefault() {
return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
}
@@ -908,6 +912,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
return true;
}
+ @VisibleForTesting
+ void setNoNetworksAvailable(boolean noNetworksAvailable) {
+ mNoNetworksAvailable = noNetworksAvailable;
+ }
+
private void updateAirplaneMode(boolean force) {
boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
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/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 8d72c9c8810e..b9b62b415c00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -106,10 +106,18 @@ public class WifiSignalController extends
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
- IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
if (mProviderModel) {
+ // WiFi icon will only be shown in the statusbar in 2 scenarios
+ // 1. WiFi is the default network, and it is validated
+ // 2. WiFi is the default network, it is not validated and there is no other
+ // non-Carrier WiFi networks available.
+ boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
+ || (mCurrentState.inetCondition == 0
+ && !mNetworkController.isNonCarrierWifiNetworkAvailable());
+ IconState statusIcon = new IconState(
+ wifiVisible && maybeShowIcons, getCurrentIconId(), contentDescription);
IconState qsIcon = null;
- if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+ if ((mCurrentState.isDefault && maybeShowIcons) || (!mNetworkController.isRadioOn()
&& !mNetworkController.isEthernetDefault())) {
qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
@@ -123,6 +131,8 @@ public class WifiSignalController extends
);
callback.setWifiIndicators(wifiIndicators);
} else {
+ IconState statusIcon = new IconState(
+ wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
: getQsCurrentIconId(), contentDescription);
@@ -146,15 +156,25 @@ public class WifiSignalController extends
if (mCurrentState.inetCondition == 0) {
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- boolean qsVisible = mCurrentState.enabled
- && (mCurrentState.connected && mCurrentState.inetCondition == 1);
-
+ // Mobile icon will only be shown in the statusbar in 2 scenarios
+ // 1. Mobile is the default network, and it is validated
+ // 2. Mobile is the default network, it is not validated and there is no other
+ // non-Carrier WiFi networks available.
+ boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
+ || (mCurrentState.inetCondition == 0
+ && !mNetworkController.isNonCarrierWifiNetworkAvailable());
+ boolean sbVisible = mCurrentState.enabled && mCurrentState.connected
+ && maybeShowIcons && mCurrentState.isDefault;
IconState statusIcon =
- new IconState(qsVisible, getCurrentIconIdForCarrierWifi(), contentDescription);
- int qsTypeIcon = mCurrentState.connected ? icons.qsDataType : 0;
- int typeIcon = mCurrentState.connected ? icons.dataType : 0;
- IconState qsIcon = new IconState(
- mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
+ new IconState(sbVisible, getCurrentIconIdForCarrierWifi(), contentDescription);
+ int typeIcon = sbVisible ? icons.dataType : 0;
+ int qsTypeIcon = 0;
+ IconState qsIcon = null;
+ if (sbVisible) {
+ qsTypeIcon = icons.qsDataType;
+ qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(),
+ contentDescription);
+ }
CharSequence description =
mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5d028454a417..f19228783b88 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -43,7 +43,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -87,12 +86,6 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
protected static final int SECONDARY = 1;
protected static final int NEUTRAL = 2;
- // If lock screen wallpaper colors should also be considered when selecting the theme.
- // Doing this has performance impact, given that overlays would need to be swapped when
- // the device unlocks.
- @VisibleForTesting
- static final boolean USE_LOCK_SCREEN_WALLPAPER = false;
-
private final ThemeOverlayApplier mThemeManager;
private final UserManager mUserManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -103,7 +96,6 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private final WallpaperManager mWallpaperManager;
private final KeyguardStateController mKeyguardStateController;
private final boolean mIsMonetEnabled;
- private WallpaperColors mLockColors;
private WallpaperColors mSystemColors;
// If fabricated overlays were already created for the current theme.
private boolean mNeedsOverlayCreation;
@@ -117,6 +109,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private FabricatedOverlay mSecondaryOverlay;
// Neutral system colors overlay
private FabricatedOverlay mNeutralOverlay;
+ // If wallpaper color event will be accepted and change the UI colors.
+ private boolean mAcceptColorEvents = true;
@Inject
public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDispatcher,
@@ -146,13 +140,20 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
- reevaluateSystemTheme(true /* forceReload */);
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())
+ || Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction())) {
+ if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
+ reevaluateSystemTheme(true /* forceReload */);
+ } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
+ mAcceptColorEvents = true;
+ Log.i(TAG, "Allowing color events again");
+ }
}
- }, filter, mBgExecutor, UserHandle.ALL);
+ }, filter, mMainExecutor, UserHandle.ALL);
mSecureSettings.registerContentObserverForUser(
Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
false,
@@ -170,38 +171,22 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
// Upon boot, make sure we have the most up to date colors
mBgExecutor.execute(() -> {
- WallpaperColors lockColors = mWallpaperManager.getWallpaperColors(
- WallpaperManager.FLAG_LOCK);
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
WallpaperManager.FLAG_SYSTEM);
mMainExecutor.execute(() -> {
- if (USE_LOCK_SCREEN_WALLPAPER) {
- mLockColors = lockColors;
- }
mSystemColors = systemColor;
reevaluateSystemTheme(false /* forceReload */);
});
});
- if (USE_LOCK_SCREEN_WALLPAPER) {
- mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- if (mLockColors == null) {
- return;
- }
- // It's possible that the user has a lock screen wallpaper. On this case we'll
- // end up with different colors after unlocking.
- reevaluateSystemTheme(false /* forceReload */);
- }
- });
- }
mWallpaperManager.addOnColorsChangedListener((wallpaperColors, which) -> {
- if (USE_LOCK_SCREEN_WALLPAPER && (which & WallpaperManager.FLAG_LOCK) != 0) {
- mLockColors = wallpaperColors;
- if (DEBUG) {
- Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which);
- }
+ if (!mAcceptColorEvents) {
+ Log.i(TAG, "Wallpaper color event rejected: " + wallpaperColors);
+ return;
}
+ if (wallpaperColors != null && mAcceptColorEvents) {
+ mAcceptColorEvents = false;
+ }
+
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
mSystemColors = wallpaperColors;
if (DEBUG) {
@@ -213,10 +198,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
}
private void reevaluateSystemTheme(boolean forceReload) {
- WallpaperColors currentColors =
- mKeyguardStateController.isShowing() && mLockColors != null
- ? mLockColors : mSystemColors;
-
+ final WallpaperColors currentColors = mSystemColors;
final int mainColor;
final int accentCandidate;
if (currentColors == null) {
@@ -378,8 +360,6 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("USE_LOCK_SCREEN_WALLPAPER=" + USE_LOCK_SCREEN_WALLPAPER);
- pw.println("mLockColors=" + mLockColors);
pw.println("mSystemColors=" + mSystemColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
@@ -388,5 +368,6 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
+ pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
}
}
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/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 4611fe03e157..78cd3a823aab 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -77,6 +77,8 @@ import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.startingsurface.StartingSurface;
+import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -373,6 +375,9 @@ public abstract class WMShellBaseModule {
return new PipUiEventLogger(uiEventLogger, packageManager);
}
+ @BindsOptionalOf
+ abstract PipTouchHandler optionalPipTouchHandler();
+
//
// Shell transitions
//
@@ -448,6 +453,22 @@ public abstract class WMShellBaseModule {
@BindsOptionalOf
abstract AppPairsController optionalAppPairs();
+ // Starting window
+
+ @WMSingleton
+ @Provides
+ static Optional<StartingSurface> provideStartingSurface(
+ StartingWindowController startingWindowController) {
+ return Optional.of(startingWindowController.asStartingSurface());
+ }
+
+ @WMSingleton
+ @Provides
+ static StartingWindowController provideStartingWindowController(Context context,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new StartingWindowController(context, mainExecutor);
+ }
+
//
// Task view factory
//
@@ -479,6 +500,8 @@ public abstract class WMShellBaseModule {
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
+ Optional<StartingSurface> startingSurface,
+ Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -488,6 +511,8 @@ public abstract class WMShellBaseModule {
legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
+ startingSurface,
+ pipTouchHandlerOptional,
fullscreenTaskListener,
transitions,
mainExecutor);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 854be1f76722..1783fa4112b8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.systemBars;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -24,13 +25,20 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowInsetsController;
+import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -45,12 +53,21 @@ import org.mockito.junit.MockitoRule;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
+ private static final int SCREEN_WIDTH = 1600;
+ private static final int FAKE_MEASURE_SPEC =
+ View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
+
+ private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
+ private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
+
+
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
private WindowInsetsController mWindowInsetsController;
+
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@@ -58,9 +75,18 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
@Before
public void setup() {
+ // Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
+ // the real references (rather than the TestableResources that this call creates).
+ mContext.ensureTestableResources();
+ FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams);
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
+ mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
@Test
@@ -69,4 +95,128 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
any(), any());
}
-} \ No newline at end of file
+
+ @Test
+ public void onMeasure_usesFullWidthWithoutOneHandedMode() {
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ false,
+ ONE_HANDED_SECURITY_MODE);
+
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ }
+
+ @Test
+ public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ true,
+ ONE_HANDED_SECURITY_MODE);
+
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ }
+
+ @Test
+ public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ false,
+ ONE_HANDED_SECURITY_MODE);
+
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ }
+
+ @Test
+ public void onMeasure_usesHalfWidthWithFlagsEnabled() {
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ true,
+ ONE_HANDED_SECURITY_MODE);
+
+ int halfWidthMeasureSpec =
+ View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
+ mKeyguardSecurityContainer.onMeasure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+
+ verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, FAKE_MEASURE_SPEC);
+ }
+
+ @Test
+ public void onMeasure_usesFullWidthForFullScreenIme() {
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ true,
+ TWO_HANDED_SECURITY_MODE);
+
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ }
+
+ @Test
+ public void onMeasure_respectsViewInsets() {
+ int imeInsetAmount = 100;
+ int systemBarInsetAmount = 10;
+
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ false,
+ ONE_HANDED_SECURITY_MODE);
+
+ Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
+ Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
+
+ WindowInsets insets = new WindowInsets.Builder()
+ .setInsets(ime(), imeInset)
+ .setInsetsIgnoringVisibility(systemBars(), systemBarInset)
+ .build();
+
+ // It's reduced by the max of the systembar and IME, so just subtract IME inset.
+ int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+ SCREEN_WIDTH - imeInsetAmount, View.MeasureSpec.EXACTLY);
+
+ mKeyguardSecurityContainer.onApplyWindowInsets(insets);
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
+ }
+
+ @Test
+ public void onMeasure_respectsViewInsets_largerSystembar() {
+ int imeInsetAmount = 0;
+ int systemBarInsetAmount = 10;
+
+ setUpKeyguard(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */ false,
+ ONE_HANDED_SECURITY_MODE);
+
+ Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
+ Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
+
+ WindowInsets insets = new WindowInsets.Builder()
+ .setInsets(ime(), imeInset)
+ .setInsetsIgnoringVisibility(systemBars(), systemBarInset)
+ .build();
+
+ int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+ SCREEN_WIDTH - systemBarInsetAmount, View.MeasureSpec.EXACTLY);
+
+ mKeyguardSecurityContainer.onApplyWindowInsets(insets);
+ mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
+ verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
+ }
+
+ private void setUpKeyguard(
+ boolean deviceConfigCanUseOneHandedKeyguard,
+ boolean sysuiResourceCanUseOneHandedKeyguard,
+ SecurityMode securityMode) {
+ TestableResources testableResources = mContext.getOrCreateTestableResources();
+ testableResources.addOverride(com.android.internal.R.bool.config_enableOneHandedKeyguard,
+ deviceConfigCanUseOneHandedKeyguard);
+ testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
+ sysuiResourceCanUseOneHandedKeyguard);
+ mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
+ }
+}
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/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 800d8593035d..f60fa099feaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -19,14 +19,21 @@ package com.android.systemui.people.widget;
import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
+import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -38,12 +45,15 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
import android.app.people.ConversationChannel;
+import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
+import android.app.people.PeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -56,7 +66,6 @@ import android.testing.AndroidTestingRunner;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
-import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.PeopleSpaceUtils;
@@ -76,7 +85,9 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
@SmallTest
@@ -99,6 +110,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
+ private static final String KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0);
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
@@ -121,12 +133,15 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Mock
private NotificationListener mListenerService;
- @Mock
- private IAppWidgetService mIAppWidgetService;
+
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
private IPeopleManager mIPeopleManager;
+ @Mock
+ private PeopleManager mPeopleManager;
+ @Mock
+ private LauncherApps mLauncherApps;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -136,13 +151,19 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
+ private PeopleSpaceWidgetProvider mProvider;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mLauncherApps = mock(LauncherApps.class);
mManager =
new PeopleSpaceWidgetManager(mContext);
- mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
+ mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
+ mLauncherApps);
mManager.attach(mListenerService);
+ mProvider = new PeopleSpaceWidgetProvider();
+ mProvider.setPeopleSpaceWidgetManager(mManager);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
@@ -166,7 +187,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false);
@@ -182,7 +203,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
int[] widgetIdsArray = {};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
Notification notificationWithoutShortcut = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
@@ -206,7 +227,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
int[] widgetIdsArray = {};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -224,7 +245,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotificationChannel channel =
new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -240,7 +261,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotificationChannel channel =
new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
@@ -257,7 +278,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -277,7 +298,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -298,7 +319,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -318,7 +339,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
.setNotification(createMessagingStyleNotification(
@@ -339,9 +360,74 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
}
@Test
+ public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
+ ACTIVITY_GAME).setDescription("Playing a game!").build();
+ ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID,
+ ACTIVITY_BIRTHDAY).build();
+ ConversationChannel conversationChannel = getConversationWithShortcutId(OTHER_SHORTCUT_ID,
+ Arrays.asList(status1, status2));
+ mManager.updateWidgetsWithConversationChanged(conversationChannel);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testUpdateStatusPostedIfExistingTile() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
+ ACTIVITY_GAME).setDescription("Playing a game!").build();
+ ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
+ Arrays.asList(status));
+ mManager.updateWidgetsWithConversationChanged(conversationChannel);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getStatuses()).containsExactly(status);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception {
+ addSecondWidgetForPersonTile();
+
+ ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID,
+ ACTIVITY_ANNIVERSARY).build();
+ ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID,
+ Arrays.asList(status));
+ mManager.updateWidgetsWithConversationChanged(conversationChannel);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
public void testUpdateNotificationPostedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -362,17 +448,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
- when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
- .thenReturn(options);
- // Set the same Person associated on another People Tile widget ID.
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
-
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
- SECOND_WIDGET_ID_WITH_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ addSecondWidgetForPersonTile();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
@@ -395,19 +471,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
throws Exception {
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
- when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
- .thenReturn(options);
- // Set the same Person associated on another People Tile widget ID.
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
- setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ addSecondWidgetForPersonTile();
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
- SECOND_WIDGET_ID_WITH_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false))
@@ -430,7 +496,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile()
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -456,7 +522,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile()
throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -480,7 +546,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Test
public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createNotification(
SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false);
@@ -502,14 +568,82 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
any());
}
+ @Test
+ public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() {
+ addSecondWidgetForPersonTile();
+ mProvider.onUpdate(mContext, mAppWidgetManager,
+ new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT});
+
+ // Delete only one widget for the conversation.
+ mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT});
+
+ // Check deleted storage.
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull();
+ assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull();
+ assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT));
+ // Check listener & shortcut caching remain for other widget.
+ verify(mPeopleManager, never()).unregisterConversationListener(any());
+ verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A),
+ eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+ eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+
+ // Delete all widgets for the conversation.
+ mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT});
+
+ // Check deleted storage.
+ SharedPreferences secondWidgetSp = mContext.getSharedPreferences(
+ String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ Context.MODE_PRIVATE);
+ assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull();
+ assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull();
+ assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID);
+ assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty();
+ // Check listener is removed and shortcut is uncached.
+ verify(mPeopleManager, times(1)).unregisterConversationListener(any());
+ verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A),
+ eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)),
+ eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS));
+ }
+
+ /**
+ * Adds another widget for {@code PERSON_TILE} with widget ID: {@code
+ * SECOND_WIDGET_ID_WITH_SHORTCUT}.
+ */
+ private void addSecondWidgetForPersonTile() {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ }
+
/**
* Returns a single conversation associated with {@code shortcutId}.
*/
private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+ return getConversationWithShortcutId(shortcutId, Arrays.asList());
+ }
+
+ /**
+ * Returns a single conversation associated with {@code shortcutId} and {@code statuses}.
+ */
+ private ConversationChannel getConversationWithShortcutId(String shortcutId,
+ List<ConversationStatus> statuses) throws Exception {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
"name").build();
ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
- 0L, false);
+ 0L, false, false, statuses);
return convo;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index 3d53062d7d02..62cc9b7e3602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -49,7 +49,7 @@ public class TileAdapterTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
TestableLooper.get(this).runWithLooper(() -> mTileAdapter =
- new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake()));
+ new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake(), /* qsFlag */false));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
new file mode 100644
index 000000000000..998e070009cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.qs.tileimpl
+
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.text.TextUtils
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSTileBaseViewTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var iconView: QSIconView
+
+ private lateinit var tileView: QSTileBaseView
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ tileView = QSTileBaseView(context, iconView, false)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_unavailable() {
+ val state = QSTile.State()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_UNAVAILABLE
+ state.secondaryLabel = testString
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_booleanInactive() {
+ val state = QSTile.BooleanState()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = testString
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_booleanActive() {
+ val state = QSTile.BooleanState()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = testString
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_availableNotBoolean_inactive() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = ""
+
+ tileView.handleStateChanged(state)
+
+ assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_availableNotBoolean_active() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = ""
+
+ tileView.handleStateChanged(state)
+
+ assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_unavailable() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_UNAVAILABLE
+ state.secondaryLabel = ""
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.tile_unavailable)
+ )
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_booleanInactive() {
+ val state = QSTile.BooleanState()
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = ""
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.switch_bar_off)
+ )
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_booleanActive() {
+ val state = QSTile.BooleanState()
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = ""
+
+ tileView.handleStateChanged(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.switch_bar_on)
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index ffd747e09e23..880c290802df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -18,10 +18,12 @@ package com.android.systemui.qs.tiles;
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
-import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -34,9 +36,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.util.settings.FakeSettings;
import org.junit.Before;
import org.junit.Test;
@@ -60,8 +62,9 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
private QSLogger mQSLogger;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private ReduceBrightColorsController mReduceBrightColorsController;
- private FakeSettings mFakeSettings;
private TestableLooper mTestableLooper;
private ReduceBrightColorsTile mTile;
@@ -72,24 +75,23 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
- mFakeSettings = new FakeSettings();
mTile = new ReduceBrightColorsTile(
true,
+ mReduceBrightColorsController,
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
mMetricsLogger,
mStatusBarStateController,
mActivityStarter,
- mQSLogger,
- mUserTracker,
- mFakeSettings
+ mQSLogger
);
}
@Test
public void testNotActive() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false);
mTile.refreshState();
mTestableLooper.processAllMessages();
@@ -100,33 +102,27 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
@Test
public void testActive() {
- mFakeSettings.putIntForUser(
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
- 1,
- mUserTracker.getUserId());
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
mTile.refreshState();
mTestableLooper.processAllMessages();
- assertActiveState();
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ assertEquals(mTile.getState().label.toString(),
+ mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
}
@Test
- public void testActive_clicked_isActive() {
+ public void testActive_clicked_featureIsActivated() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false);
mTile.refreshState();
mTestableLooper.processAllMessages();
// Validity check
assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
mTile.handleClick();
- mTile.refreshState();
- mTestableLooper.processAllMessages();
- assertActiveState();
+ verify(mReduceBrightColorsController, times(1))
+ .setReduceBrightColorsActivated(eq(true));
}
- private void assertActiveState() {
- assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
- assertEquals(mTile.getState().label.toString(),
- mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
- }
}
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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 82d1f43e5e4e..094a70e24572 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -47,6 +49,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -67,6 +70,8 @@ import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
+import javax.inject.Named;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -88,9 +93,11 @@ public class AutoTileManagerTest extends SysuiTestCase {
@Mock private DataSaverController mDataSaverController;
@Mock private ManagedProfileController mManagedProfileController;
@Mock private NightDisplayListener mNightDisplayListener;
+ @Mock private ReduceBrightColorsController mReduceBrightColorsController;
@Mock(answer = Answers.RETURNS_SELF)
private AutoAddTracker.Builder mAutoAddTrackerBuilder;
@Mock private Context mUserContext;
+ private final boolean mIsReduceBrightColorsAvailable = true;
private AutoTileManager mAutoTileManager;
private SecureSettings mSecureSettings;
@@ -130,7 +137,9 @@ public class AutoTileManagerTest extends SysuiTestCase {
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
NightDisplayListener nightDisplayListener,
- CastController castController) {
+ CastController castController,
+ ReduceBrightColorsController reduceBrightColorsController,
+ @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()),
mSecureSettings,
@@ -138,13 +147,15 @@ public class AutoTileManagerTest extends SysuiTestCase {
dataSaverController,
managedProfileController,
nightDisplayListener,
- castController);
+ castController,
+ reduceBrightColorsController,
+ isReduceBrightColorsAvailable);
}
private AutoTileManager createAutoTileManager(Context context) {
return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
mDataSaverController, mManagedProfileController, mNightDisplayListener,
- mCastController);
+ mCastController, mReduceBrightColorsController, mIsReduceBrightColorsAvailable);
}
@Test
@@ -157,9 +168,11 @@ public class AutoTileManagerTest extends SysuiTestCase {
ManagedProfileController mPC = mock(ManagedProfileController.class);
NightDisplayListener nDS = mock(NightDisplayListener.class);
CastController cC = mock(CastController.class);
+ ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
AutoTileManager manager =
- createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC);
+ createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC,
+ true);
verify(tracker, never()).initialize();
verify(hC, never()).addCallback(any());
@@ -167,6 +180,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(mPC, never()).addCallback(any());
verify(nDS, never()).setCallback(any());
verify(cC, never()).addCallback(any());
+ verify(rBC, never()).addCallback(any());
assertNull(manager.getSecureSettingForKey(TEST_SETTING));
assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
}
@@ -207,6 +221,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
}
+ InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
+ inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
+ inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
+
InOrder inOrderCast = inOrder(mCastController);
inOrderCast.verify(mCastController).removeCallback(any());
inOrderCast.verify(mCastController).addCallback(any());
@@ -247,6 +265,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
}
+ InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
+ inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
+ inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
+
InOrder inOrderCast = inOrder(mCastController);
inOrderCast.verify(mCastController).removeCallback(any());
inOrderCast.verify(mCastController, never()).addCallback(any());
@@ -315,6 +337,18 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(mQsTileHost, never()).addTile("night");
}
+ @Test
+ public void reduceBrightColorsTileAdded_whenActivated() {
+ mAutoTileManager.mReduceBrightColorsCallback.onActivated(true);
+ verify(mQsTileHost).addTile("reduce_brightness");
+ }
+
+ @Test
+ public void reduceBrightColorsTileNotAdded_whenDeactivated() {
+ mAutoTileManager.mReduceBrightColorsCallback.onActivated(false);
+ verify(mQsTileHost, never()).addTile("reduce_brightness");
+ }
+
private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
CastDevice cd = new CastDevice();
cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index dd31f522b94d..ec5114e181e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -71,10 +71,8 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -222,10 +220,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private FeatureFlags mFeatureFlags;
@Mock
- private ControlsComponent mControlsComponent;
- @Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
private AmbientState mAmbientState;
@Mock
private UserManager mUserManager;
@@ -328,9 +322,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mUserManager,
mMediaDataManager,
mAmbientState,
- mFeatureFlags,
- mControlsComponent,
- mBroadcastDispatcher);
+ mFeatureFlags);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index e52b92648670..999d2822c928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -50,11 +50,11 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
@@ -106,7 +106,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected NetworkControllerImpl mNetworkController;
protected MobileSignalController mMobileSignalController;
- protected PhoneStateListener mPhoneStateListener;
protected SignalStrength mSignalStrength;
protected ServiceState mServiceState;
protected TelephonyDisplayInfo mTelephonyDisplayInfo;
@@ -250,8 +249,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
setDefaultSubId(mSubId);
setSubscriptions(mSubId);
mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
- mPhoneStateListener = mMobileSignalController.mMobileStatusTracker.getPhoneStateListener();
-
ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mMockCm, atLeastOnce())
@@ -372,6 +369,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
mNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
+ mock(Network.class), new NetworkCapabilities(mNetCapabilities));
} else {
mNetworkCallback.onLost(mock(Network.class));
}
@@ -453,18 +452,16 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
private void updateSignalStrength() {
Log.d(TAG, "Sending Signal Strength: " + mSignalStrength);
- mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength);
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onSignalStrengthsChanged(mSignalStrength);
}
protected void updateServiceState() {
Log.d(TAG, "Sending Service State: " + mServiceState);
- mPhoneStateListener.onServiceStateChanged(mServiceState);
- mPhoneStateListener.onDisplayInfoChanged(mTelephonyDisplayInfo);
- }
-
- public void updateCallState(int state) {
- // Inputs not currently used in NetworkControllerImpl.
- mPhoneStateListener.onCallStateChanged(state, "0123456789");
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onServiceStateChanged(mServiceState);
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onDisplayInfoChanged(mTelephonyDisplayInfo);
}
public void updateDataConnectionState(int dataState, int dataNetType) {
@@ -476,16 +473,19 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
.thenReturn(fakeRegInfo);
when(mTelephonyDisplayInfo.getNetworkType()).thenReturn(dataNetType);
- mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onDataConnectionStateChanged(dataState, dataNetType);
}
public void updateDataActivity(int dataActivity) {
- mPhoneStateListener.onDataActivity(dataActivity);
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onDataActivity(dataActivity);
}
public void setCarrierNetworkChange(boolean enable) {
Log.d(TAG, "setCarrierNetworkChange(" + enable + ")");
- mPhoneStateListener.onCarrierNetworkChange(enable);
+ mMobileSignalController.mMobileStatusTracker.getTelephonyCallback()
+ .onCarrierNetworkChange(enable);
}
protected void verifyHasNoSims(boolean hasNoSimsVisible) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index c6812a26c20b..847030e83115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -223,13 +223,14 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
setWifiEnabled(true);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
+ mNetworkController.setNoNetworksAvailable(false);
setWifiStateForVcn(true, testSsid);
setWifiLevelForVcn(0);
-
// Connected, but still not validated - does not show
//verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
- verifyLastMobileDataIndicatorsForVcn(false, 0, TelephonyIcons.ICON_CWF, false);
+ verifyLastMobileDataIndicatorsForVcn(false, 0, 0, false);
+ mNetworkController.setNoNetworksAvailable(true);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevelForVcn(testLevel);
@@ -239,7 +240,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
setConnectivityViaBroadcastForVcn(
NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
- verifyLastMobileDataIndicatorsForVcn(false, testLevel, TelephonyIcons.ICON_CWF, false);
+ verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index d80c40fcd07b..8a0ac1111b59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -19,13 +19,13 @@ package com.android.systemui.theme;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
-import static com.android.systemui.theme.ThemeOverlayController.USE_LOCK_SCREEN_WALLPAPER;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -33,6 +33,8 @@ import static org.mockito.Mockito.when;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayIdentifier;
import android.graphics.Color;
@@ -91,7 +93,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
@Mock
private FeatureFlags mFeatureFlags;
@Captor
- private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback;
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver;
@Captor
private ArgumentCaptor<WallpaperManager.OnColorsChangedListener> mColorsListener;
@@ -114,12 +116,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
};
mThemeOverlayController.start();
- if (USE_LOCK_SCREEN_WALLPAPER) {
- verify(mKeyguardStateController).addCallback(
- mKeyguardStateControllerCallback.capture());
- }
verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
eq(UserHandle.USER_ALL));
+ verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiver.capture(), any(),
+ eq(mMainExecutor), any());
verify(mDumpManager).registerDumpable(any(), any());
}
@@ -129,7 +129,6 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
verify(mBgExecutor).execute(registrationRunnable.capture());
registrationRunnable.getValue().run();
- verify(mWallpaperManager).getWallpaperColors(eq(WallpaperManager.FLAG_LOCK));
verify(mWallpaperManager).getWallpaperColors(eq(WallpaperManager.FLAG_SYSTEM));
}
@@ -156,6 +155,18 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
// Should not ask again if changed to same value
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
verifyNoMoreInteractions(mThemeOverlayApplier);
+
+ // Should not ask again even for new colors until we change wallpapers
+ mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
+ null, null), WallpaperManager.FLAG_SYSTEM);
+ verifyNoMoreInteractions(mThemeOverlayApplier);
+
+ // But should change theme after changing wallpapers
+ clearInvocations(mThemeOverlayApplier);
+ mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
+ null, null), WallpaperManager.FLAG_SYSTEM);
+ verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
}
@Test
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/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index bafb641dcc9e..6828dd916701 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -40,29 +40,34 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
private final IAccessibilityInteractionConnectionCallback mServiceCallback;
private final IAccessibilityInteractionConnection mConnectionWithReplacementActions;
private final int mInteractionId;
+ private final int mNodeWithReplacementActionsInteractionId;
private final Object mLock = new Object();
@GuardedBy("mLock")
- List<AccessibilityNodeInfo> mNodesWithReplacementActions;
+ private boolean mReplacementNodeIsReadyOrFailed;
+
+ @GuardedBy("mLock")
+ AccessibilityNodeInfo mNodeWithReplacementActions;
@GuardedBy("mLock")
List<AccessibilityNodeInfo> mNodesFromOriginalWindow;
@GuardedBy("mLock")
+ boolean mSetFindNodeFromOriginalWindowCalled = false;
+
+ @GuardedBy("mLock")
AccessibilityNodeInfo mNodeFromOriginalWindow;
- // Keep track of whether or not we've been called back for a single node
@GuardedBy("mLock")
- boolean mSingleNodeCallbackHappened;
+ boolean mSetFindNodesFromOriginalWindowCalled = false;
+
- // Keep track of whether or not we've been called back for multiple node
@GuardedBy("mLock")
- boolean mMultiNodeCallbackHappened;
+ List<AccessibilityNodeInfo> mPrefetchedNodesFromOriginalWindow;
- // We shouldn't get any more callbacks after we've called back the original service, but
- // keep track to make sure we catch such strange things
@GuardedBy("mLock")
- boolean mDone;
+ boolean mSetPrefetchFromOriginalWindowCalled = false;
+
public ActionReplacingCallback(IAccessibilityInteractionConnectionCallback serviceCallback,
IAccessibilityInteractionConnection connectionWithReplacementActions,
@@ -70,19 +75,20 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
mServiceCallback = serviceCallback;
mConnectionWithReplacementActions = connectionWithReplacementActions;
mInteractionId = interactionId;
+ mNodeWithReplacementActionsInteractionId = interactionId + 1;
// Request the root node of the replacing window
final long identityToken = Binder.clearCallingIdentity();
try {
mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId(
- AccessibilityNodeInfo.ROOT_NODE_ID, null, interactionId + 1, this, 0,
+ AccessibilityNodeInfo.ROOT_NODE_ID, null,
+ mNodeWithReplacementActionsInteractionId, this, 0,
interrogatingPid, interrogatingTid, null, null);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
}
- // Pretend we already got a (null) list of replacement nodes
- mMultiNodeCallbackHappened = true;
+ mReplacementNodeIsReadyOrFailed = true;
} finally {
Binder.restoreCallingIdentity(identityToken);
}
@@ -90,46 +96,67 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
@Override
public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId) {
- boolean readyForCallback;
- synchronized(mLock) {
+ synchronized (mLock) {
if (interactionId == mInteractionId) {
mNodeFromOriginalWindow = info;
+ mSetFindNodeFromOriginalWindowCalled = true;
+ } else if (interactionId == mNodeWithReplacementActionsInteractionId) {
+ mNodeWithReplacementActions = info;
+ mReplacementNodeIsReadyOrFailed = true;
} else {
Slog.e(LOG_TAG, "Callback with unexpected interactionId");
return;
}
-
- mSingleNodeCallbackHappened = true;
- readyForCallback = mMultiNodeCallbackHappened;
- }
- if (readyForCallback) {
- replaceInfoActionsAndCallService();
}
+ replaceInfoActionsAndCallServiceIfReady();
}
@Override
public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
int interactionId) {
- boolean callbackForSingleNode;
- boolean callbackForMultipleNodes;
- synchronized(mLock) {
+ synchronized (mLock) {
if (interactionId == mInteractionId) {
mNodesFromOriginalWindow = infos;
- } else if (interactionId == mInteractionId + 1) {
- mNodesWithReplacementActions = infos;
+ mSetFindNodesFromOriginalWindowCalled = true;
+ } else if (interactionId == mNodeWithReplacementActionsInteractionId) {
+ setNodeWithReplacementActionsFromList(infos);
+ mReplacementNodeIsReadyOrFailed = true;
} else {
Slog.e(LOG_TAG, "Callback with unexpected interactionId");
return;
}
- callbackForSingleNode = mSingleNodeCallbackHappened;
- callbackForMultipleNodes = mMultiNodeCallbackHappened;
- mMultiNodeCallbackHappened = true;
}
- if (callbackForSingleNode) {
- replaceInfoActionsAndCallService();
+ replaceInfoActionsAndCallServiceIfReady();
+ }
+
+ @Override
+ public void setPrefetchAccessibilityNodeInfoResult(List<AccessibilityNodeInfo> infos,
+ int interactionId)
+ throws RemoteException {
+ synchronized (mLock) {
+ if (interactionId == mInteractionId) {
+ mPrefetchedNodesFromOriginalWindow = infos;
+ mSetPrefetchFromOriginalWindowCalled = true;
+ } else {
+ Slog.e(LOG_TAG, "Callback with unexpected interactionId");
+ return;
+ }
}
- if (callbackForMultipleNodes) {
- replaceInfosActionsAndCallService();
+ replaceInfoActionsAndCallServiceIfReady();
+ }
+
+ private void replaceInfoActionsAndCallServiceIfReady() {
+ replaceInfoActionsAndCallService();
+ replaceInfosActionsAndCallService();
+ replacePrefetchInfosActionsAndCallService();
+ }
+
+ private void setNodeWithReplacementActionsFromList(List<AccessibilityNodeInfo> infos) {
+ for (int i = 0; i < infos.size(); i++) {
+ AccessibilityNodeInfo info = infos.get(i);
+ if (info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID) {
+ mNodeWithReplacementActions = info;
+ }
}
}
@@ -142,55 +169,81 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
private void replaceInfoActionsAndCallService() {
final AccessibilityNodeInfo nodeToReturn;
+ boolean doCallback = false;
synchronized (mLock) {
- if (mDone) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "Extra callback");
- }
- return;
- }
- if (mNodeFromOriginalWindow != null) {
+ doCallback = mReplacementNodeIsReadyOrFailed
+ && mSetFindNodeFromOriginalWindowCalled;
+ if (doCallback && mNodeFromOriginalWindow != null) {
replaceActionsOnInfoLocked(mNodeFromOriginalWindow);
+ mSetFindNodeFromOriginalWindowCalled = false;
}
- recycleReplaceActionNodesLocked();
nodeToReturn = mNodeFromOriginalWindow;
- mDone = true;
}
- try {
- mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId);
- } catch (RemoteException re) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult");
+ if (doCallback) {
+ try {
+ mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult");
+ }
}
}
}
private void replaceInfosActionsAndCallService() {
- final List<AccessibilityNodeInfo> nodesToReturn;
+ List<AccessibilityNodeInfo> nodesToReturn = null;
+ boolean doCallback = false;
synchronized (mLock) {
- if (mDone) {
+ doCallback = mReplacementNodeIsReadyOrFailed
+ && mSetFindNodesFromOriginalWindowCalled;
+ if (doCallback) {
+ nodesToReturn = replaceActionsLocked(mNodesFromOriginalWindow);
+ mSetFindNodesFromOriginalWindowCalled = false;
+ }
+ }
+ if (doCallback) {
+ try {
+ mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId);
+ } catch (RemoteException re) {
if (DEBUG) {
- Slog.e(LOG_TAG, "Extra callback");
+ Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
}
- return;
}
- if (mNodesFromOriginalWindow != null) {
- for (int i = 0; i < mNodesFromOriginalWindow.size(); i++) {
- replaceActionsOnInfoLocked(mNodesFromOriginalWindow.get(i));
+ }
+ }
+
+ private void replacePrefetchInfosActionsAndCallService() {
+ List<AccessibilityNodeInfo> nodesToReturn = null;
+ boolean doCallback = false;
+ synchronized (mLock) {
+ doCallback = mReplacementNodeIsReadyOrFailed
+ && mSetPrefetchFromOriginalWindowCalled;
+ if (doCallback) {
+ nodesToReturn = replaceActionsLocked(mPrefetchedNodesFromOriginalWindow);
+ mSetPrefetchFromOriginalWindowCalled = false;
+ }
+ }
+ if (doCallback) {
+ try {
+ mServiceCallback.setPrefetchAccessibilityNodeInfoResult(
+ nodesToReturn, mInteractionId);
+ } catch (RemoteException re) {
+ if (DEBUG) {
+ Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
}
}
- recycleReplaceActionNodesLocked();
- nodesToReturn = (mNodesFromOriginalWindow == null)
- ? null : new ArrayList<>(mNodesFromOriginalWindow);
- mDone = true;
}
- try {
- mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId);
- } catch (RemoteException re) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult");
+ }
+
+ @GuardedBy("mLock")
+ private List<AccessibilityNodeInfo> replaceActionsLocked(List<AccessibilityNodeInfo> infos) {
+ if (infos != null) {
+ for (int i = 0; i < infos.size(); i++) {
+ replaceActionsOnInfoLocked(infos.get(i));
}
}
+ return (infos == null)
+ ? null : new ArrayList<>(infos);
}
@GuardedBy("mLock")
@@ -204,40 +257,22 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection
info.setDismissable(false);
// We currently only replace actions for the root node
if ((info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID)
- && mNodesWithReplacementActions != null) {
- // This list should always contain a single node with the root ID
- for (int i = 0; i < mNodesWithReplacementActions.size(); i++) {
- AccessibilityNodeInfo nodeWithReplacementActions =
- mNodesWithReplacementActions.get(i);
- if (nodeWithReplacementActions.getSourceNodeId()
- == AccessibilityNodeInfo.ROOT_NODE_ID) {
- List<AccessibilityAction> actions = nodeWithReplacementActions.getActionList();
- if (actions != null) {
- for (int j = 0; j < actions.size(); j++) {
- info.addAction(actions.get(j));
- }
- // The PIP needs to be able to take accessibility focus
- info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
- info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
- }
- info.setClickable(nodeWithReplacementActions.isClickable());
- info.setFocusable(nodeWithReplacementActions.isFocusable());
- info.setContextClickable(nodeWithReplacementActions.isContextClickable());
- info.setScrollable(nodeWithReplacementActions.isScrollable());
- info.setLongClickable(nodeWithReplacementActions.isLongClickable());
- info.setDismissable(nodeWithReplacementActions.isDismissable());
+ && mNodeWithReplacementActions != null) {
+ List<AccessibilityAction> actions = mNodeWithReplacementActions.getActionList();
+ if (actions != null) {
+ for (int j = 0; j < actions.size(); j++) {
+ info.addAction(actions.get(j));
}
+ // The PIP needs to be able to take accessibility focus
+ info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS);
+ info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
}
+ info.setClickable(mNodeWithReplacementActions.isClickable());
+ info.setFocusable(mNodeWithReplacementActions.isFocusable());
+ info.setContextClickable(mNodeWithReplacementActions.isContextClickable());
+ info.setScrollable(mNodeWithReplacementActions.isScrollable());
+ info.setLongClickable(mNodeWithReplacementActions.isLongClickable());
+ info.setDismissable(mNodeWithReplacementActions.isDismissable());
}
}
-
- @GuardedBy("mLock")
- private void recycleReplaceActionNodesLocked() {
- if (mNodesWithReplacementActions == null) return;
- for (int i = mNodesWithReplacementActions.size() - 1; i >= 0; i--) {
- AccessibilityNodeInfo nodeWithReplacementAction = mNodesWithReplacementActions.get(i);
- nodeWithReplacementAction.recycle();
- }
- mNodesWithReplacementActions = null;
- }
}
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/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java b/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
index feed18d438c7..3d8f5173d25a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
@@ -98,8 +98,7 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene
}
mProcessMotionEvent = true;
for (int i = 0; i < mGestureMatchers.size(); i++) {
- final GestureMatcher matcher =
- mGestureMatchers.get(i);
+ final GestureMatcher matcher = mGestureMatchers.get(i);
matcher.onMotionEvent(event, rawEvent, policyFlags);
if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
clear();
@@ -128,7 +127,10 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene
MotionEvent rawEvent, int policyFlags) {
if (state == GestureMatcher.STATE_GESTURE_COMPLETED) {
mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags);
- //Clear the states in onMotionEvent().
+ // Ideally we clear the states in onMotionEvent(), this case is for hold gestures.
+ // If we clear before processing up event , then MultiTap matcher cancels the gesture
+ // due to incorrect state. It ends up listener#onGestureCancelled is called even
+ // the gesture is detected.
if (!mProcessMotionEvent) {
clear();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
index 7a4d9e34b657..570e0ce5490d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
@@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy;
class MagnificationGestureMatcher {
private static final int GESTURE_BASE = 100;
- public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;
+ public static final int GESTURE_TWO_FINGERS_DOWN_OR_SWIPE = GESTURE_BASE + 1;
public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4;
@@ -41,7 +41,7 @@ class MagnificationGestureMatcher {
public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6;
@IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
- GESTURE_TWO_FINGER_DOWN,
+ GESTURE_TWO_FINGERS_DOWN_OR_SWIPE,
GESTURE_SWIPE
})
@Retention(RetentionPolicy.SOURCE)
@@ -57,8 +57,8 @@ class MagnificationGestureMatcher {
switch (gestureId) {
case GESTURE_SWIPE:
return "GESTURE_SWIPE";
- case GESTURE_TWO_FINGER_DOWN:
- return "GESTURE_TWO_FINGER_DOWN";
+ case GESTURE_TWO_FINGERS_DOWN_OR_SWIPE:
+ return "GESTURE_TWO_FINGERS_DOWN_OR_SWIPE";
case GESTURE_SINGLE_TAP:
return "GESTURE_SINGLE_TAP";
case GESTURE_SINGLE_TAP_AND_HOLD:
@@ -71,6 +71,12 @@ class MagnificationGestureMatcher {
return "none";
}
+ /**
+ * @param context
+ * @return the duration in milliseconds between the first tap's down event and
+ * the second tap's down event to be considered that the user is going to performing
+ * panning/scaling gesture.
+ */
static int getMagnificationMultiTapTimeout(Context context) {
return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger(
R.integer.config_screen_magnification_multi_tap_adjustment);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
index a209086ba475..085c343ff631 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
@@ -65,7 +65,7 @@ class MagnificationGesturesObserver implements GesturesObserver.Listener {
* the last event before timeout.
*
* @see MagnificationGestureMatcher#GESTURE_SWIPE
- * @see MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN
+ * @see MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
*/
void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime,
List<MotionEventInfo> delayedEventQueue, MotionEvent event);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java b/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
index cd5061fa3163..fa15ac1c0e4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
@@ -49,6 +49,11 @@ class SimpleSwipe extends GestureMatcher {
}
@Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (gestureMatched(event, rawEvent, policyFlags)) {
completeGesture(event, rawEvent, policyFlags);
@@ -65,7 +70,7 @@ class SimpleSwipe extends GestureMatcher {
}
private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- return mLastDown != null && (distance(mLastDown, event) >= mSwipeMinDistance);
+ return mLastDown != null && (distance(mLastDown, event) > mSwipeMinDistance);
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java
deleted file mode 100644
index 173a5b82e003..000000000000
--- a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.accessibility.magnification;
-
-import android.content.Context;
-import android.os.Handler;
-import android.view.MotionEvent;
-
-import com.android.server.accessibility.gestures.GestureMatcher;
-
-/**
- *
- * This class is responsible for matching two fingers down gestures. The gesture matching
- * result is determined in a duration.
- */
-final class TwoFingersDown extends GestureMatcher {
-
- private MotionEvent mLastDown;
- private final int mDetectionDurationMillis;
-
- TwoFingersDown(Context context) {
- super(MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN,
- new Handler(context.getMainLooper()), null);
- mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
- context);
- }
-
- @Override
- protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- mLastDown = MotionEvent.obtain(event);
- cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags);
- }
-
- @Override
- protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mLastDown == null) {
- cancelGesture(event, rawEvent, policyFlags);
- }
- completeGesture(event, rawEvent, policyFlags);
- }
-
- @Override
- protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelGesture(event, rawEvent, policyFlags);
- }
-
- @Override
- public void clear() {
- if (mLastDown != null) {
- mLastDown.recycle();
- mLastDown = null;
- }
- super.clear();
- }
-
- @Override
- protected String getGestureName() {
- return this.getClass().getSimpleName();
- }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java
new file mode 100644
index 000000000000..1742bd46d865
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java
@@ -0,0 +1,123 @@
+/*
+ * 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.magnification;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.server.accessibility.gestures.GestureMatcher;
+
+/**
+ * This class is responsible for detecting that the user is using two fingers to perform
+ * swiping gestures or just stay pressed on the screen. The gesture matching result is determined
+ * in a duration.
+ */
+final class TwoFingersDownOrSwipe extends GestureMatcher {
+
+ private final int mDoubleTapTimeout;
+ private final int mDetectionDurationMillis;
+ private final int mSwipeMinDistance;
+ private MotionEvent mFirstPointerDown;
+ private MotionEvent mSecondPointerDown;
+
+ TwoFingersDownOrSwipe(Context context) {
+ super(MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE,
+ new Handler(context.getMainLooper()), null);
+ mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
+ context);
+ mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+ mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
+
+ }
+
+ @Override
+ protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ mFirstPointerDown = MotionEvent.obtain(event);
+ cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags);
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mFirstPointerDown == null) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+ if (event.getPointerCount() == 2) {
+ mSecondPointerDown = MotionEvent.obtain(event);
+ completeAfter(mDoubleTapTimeout, event, rawEvent, policyFlags);
+ } else {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mFirstPointerDown == null || mSecondPointerDown == null) {
+ return;
+ }
+ if (distance(mFirstPointerDown, /* move */ event) > mSwipeMinDistance) {
+ completeGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (distance(mSecondPointerDown, /* move */ event) > mSwipeMinDistance) {
+ // The second pointer is swiping.
+ completeGesture(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
+ public void clear() {
+ if (mFirstPointerDown != null) {
+ mFirstPointerDown.recycle();
+ mFirstPointerDown = null;
+ }
+ if (mSecondPointerDown != null) {
+ mSecondPointerDown.recycle();
+ mSecondPointerDown = null;
+ }
+ super.clear();
+ }
+
+ @Override
+ protected String getGestureName() {
+ return this.getClass().getSimpleName();
+ }
+
+ private static double distance(@NonNull MotionEvent downEvent, @NonNull MotionEvent moveEvent) {
+ final int downActionIndex = downEvent.getActionIndex();
+ final int downPointerId = downEvent.getPointerId(downActionIndex);
+ final int moveActionIndex = moveEvent.findPointerIndex(downPointerId);
+ if (moveActionIndex < 0) {
+ return -1;
+ }
+ return MathUtils.dist(downEvent.getX(downActionIndex), downEvent.getY(downActionIndex),
+ moveEvent.getX(moveActionIndex), moveEvent.getY(moveActionIndex));
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 55a911eea821..fa3406217fa8 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -323,7 +323,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
* this state is as follows:
* <ol>
- * <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN} is detected,
+ * <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE} is detected,
* {@link State} will be transited to {@link PanningScalingGestureState}.</li>
* <li> If other gesture is detected and the last motion event is neither ACTION_UP nor
* ACTION_CANCEL.
@@ -357,7 +357,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
new SimpleSwipe(context),
multiTap,
multiTapAndHold,
- new TwoFingersDown(context));
+ new TwoFingersDownOrSwipe(context));
}
@Override
@@ -399,7 +399,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
Slog.d(mLogTag,
"onGestureDetected : delayedEventQueue = " + delayedEventQueue);
}
- if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN
+ if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
&& mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 50ad6617b1fe..e251700498ee 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -19,6 +19,7 @@ package com.android.server.appwidget;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.res.Resources.ID_NULL;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -2578,9 +2579,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
info.updatePeriodMillis = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
info.initialLayout = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+ com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, ID_NULL);
info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
- AppWidgetProviderInfo_initialKeyguardLayout, 0);
+ AppWidgetProviderInfo_initialKeyguardLayout, ID_NULL);
String className = sa
.getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
@@ -2591,11 +2592,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
info.label = activityInfo.loadLabel(pm).toString();
info.icon = activityInfo.getIconResource();
info.previewImage = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, ID_NULL);
info.previewLayout = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, 0);
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, ID_NULL);
info.autoAdvanceViewId = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+ com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId,
+ View.NO_ID);
info.resizeMode = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
AppWidgetProviderInfo.RESIZE_NONE);
@@ -2605,8 +2607,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
info.widgetFeatures = sa.getInt(
com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
info.descriptionRes = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_description,
- Resources.ID_NULL);
+ com.android.internal.R.styleable.AppWidgetProviderInfo_description, ID_NULL);
sa.recycle();
return info;
} catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 21cae453d702..52237c934797 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -38,6 +38,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainRunna
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
+import android.Manifest;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -409,6 +410,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
checkCallerIsSystemOr(callingPackage);
int userId = getCallingUserId();
checkUsesFeature(callingPackage, userId);
+ checkProfilePermissions(request);
mFindDeviceCallback = callback;
mRequest = request;
@@ -519,6 +521,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
+ private void checkProfilePermissions(AssociationRequest request) {
+ checkProfilePermission(request,
+ AssociationRequest.DEVICE_PROFILE_WATCH,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
+ }
+
+ private void checkProfilePermission(
+ AssociationRequest request, String profile, String permission) {
+ if (profile.equals(request.getDeviceProfile())
+ && getContext().checkCallingOrSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Using " + profile + " requires " + permission);
+ }
+ }
+
@Override
public PendingIntent requestNotificationAccess(ComponentName component)
throws RemoteException {
@@ -1330,7 +1347,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 220f87db8bd4..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;
@@ -88,7 +90,6 @@ import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.GlobalWhitelistState;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -101,6 +102,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -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,13 +616,16 @@ 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,
- @NonNull ComponentName componentName, int sessionId, int flags,
- @NonNull IResultReceiver result) {
- Preconditions.checkNotNull(activityToken);
- Preconditions.checkNotNull(sessionId);
+ @NonNull IBinder shareableActivityToken, @NonNull ComponentName componentName,
+ int sessionId, int flags, @NonNull IResultReceiver result) {
+ Objects.requireNonNull(activityToken);
+ Objects.requireNonNull(shareableActivityToken);
+ Objects.requireNonNull(sessionId);
final int userId = UserHandle.getCallingUserId();
final ActivityPresentationInfo activityPresentationInfo = getAmInternal()
@@ -617,14 +637,14 @@ public final class ContentCaptureManagerService extends
setClientState(result, STATE_DISABLED, /* binder= */ null);
return;
}
- service.startSessionLocked(activityToken, activityPresentationInfo, sessionId,
- Binder.getCallingUid(), flags, result);
+ service.startSessionLocked(activityToken, shareableActivityToken,
+ activityPresentationInfo, sessionId, Binder.getCallingUid(), flags, result);
}
}
@Override
public void finishSession(int sessionId) {
- Preconditions.checkNotNull(sessionId);
+ Objects.requireNonNull(sessionId);
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
@@ -650,7 +670,7 @@ public final class ContentCaptureManagerService extends
@Override
public void removeData(@NonNull DataRemovalRequest request) {
- Preconditions.checkNotNull(request);
+ Objects.requireNonNull(request);
assertCalledByPackageOwner(request.getPackageName());
final int userId = UserHandle.getCallingUserId();
@@ -663,8 +683,8 @@ public final class ContentCaptureManagerService extends
@Override
public void shareData(@NonNull DataShareRequest request,
@NonNull IDataShareWriteAdapter clientAdapter) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(clientAdapter);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(clientAdapter);
assertCalledByPackageOwner(request.getPackageName());
@@ -754,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;
@@ -1217,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 ea68e190ff1f..225a8d48114b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -35,6 +35,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
+import android.app.assist.ActivityId;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.ComponentName;
@@ -241,6 +242,7 @@ final class ContentCapturePerUserService
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
+ @NonNull IBinder shareableActivityToken,
@NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid,
int flags, @NonNull IResultReceiver clientReceiver) {
if (activityPresentationInfo == null) {
@@ -340,8 +342,8 @@ final class ContentCapturePerUserService
mRemoteService.ensureBoundLocked();
final ContentCaptureServerSession newSession = new ContentCaptureServerSession(mLock,
- activityToken, this, componentName, clientReceiver, taskId, displayId, sessionId,
- uid, flags);
+ activityToken, new ActivityId(taskId, shareableActivityToken), this, componentName,
+ clientReceiver, taskId, displayId, sessionId, uid, flags);
if (mMaster.verbose) {
Slog.v(TAG, "startSession(): new session for "
+ ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
@@ -595,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;
@@ -669,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/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 06ab426c6698..9f3045e68550 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -25,6 +25,7 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_RE
import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_UPDATING;
import android.annotation.NonNull;
+import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.IBinder;
@@ -75,9 +76,9 @@ final class ContentCaptureServerSession {
public final ComponentName appComponentName;
ContentCaptureServerSession(@NonNull Object lock, @NonNull IBinder activityToken,
- @NonNull ContentCapturePerUserService service, @NonNull ComponentName appComponentName,
- @NonNull IResultReceiver sessionStateReceiver, int taskId, int displayId, int sessionId,
- int uid, int flags) {
+ @NonNull ActivityId activityId, @NonNull ContentCapturePerUserService service,
+ @NonNull ComponentName appComponentName, @NonNull IResultReceiver sessionStateReceiver,
+ int taskId, int displayId, int sessionId, int uid, int flags) {
Preconditions.checkArgument(sessionId != NO_SESSION_ID);
mLock = lock;
mActivityToken = activityToken;
@@ -86,7 +87,7 @@ final class ContentCaptureServerSession {
mId = sessionId;
mUid = uid;
mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
- appComponentName, taskId, displayId, flags);
+ activityId, appComponentName, displayId, flags);
mSessionStateReceiver = sessionStateReceiver;
try {
sessionStateReceiver.asBinder().linkToDeath(() -> onClientDeath(), 0);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b67bdc20f7fa..8ccfad6fe061 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",
@@ -222,7 +223,6 @@ filegroup {
"java/com/android/server/TestNetworkService.java",
"java/com/android/server/connectivity/AutodestructReference.java",
"java/com/android/server/connectivity/ConnectivityConstants.java",
- "java/com/android/server/connectivity/DataConnectionStats.java",
"java/com/android/server/connectivity/DnsManager.java",
"java/com/android/server/connectivity/KeepaliveTracker.java",
"java/com/android/server/connectivity/LingerMonitor.java",
diff --git a/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index fe3042d9da54..d83e2fd2090d 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.system.OsConstants.O_RDONLY;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -24,12 +26,15 @@ import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.provider.Downloads;
+import android.system.ErrnoException;
+import android.system.Os;
import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -46,11 +51,15 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
@@ -116,6 +125,12 @@ public class BootReceiver extends BroadcastReceiver {
private static final String METRIC_SYSTEM_SERVER = "shutdown_system_server";
private static final String METRIC_SHUTDOWN_TIME_START = "begin_shutdown";
+ // Location of ftrace pipe for notifications from kernel memory tools like KFENCE and KASAN.
+ private static final String ERROR_REPORT_TRACE_PIPE =
+ "/sys/kernel/tracing/instances/bootreceiver/trace_pipe";
+ // Avoid reporing the same bug from processDmesg() twice.
+ private static String sLastReportedBug = null;
+
@Override
public void onReceive(final Context context, Intent intent) {
// Log boot events in the background to avoid blocking the main thread with I/O
@@ -143,6 +158,209 @@ public class BootReceiver extends BroadcastReceiver {
}
}.start();
+
+ FileDescriptor tracefd = null;
+ try {
+ tracefd = Os.open(ERROR_REPORT_TRACE_PIPE, O_RDONLY, 0600);
+ } catch (ErrnoException e) {
+ Slog.wtf(TAG, "Could not open " + ERROR_REPORT_TRACE_PIPE, e);
+ return;
+ }
+
+ /*
+ * Event listener to watch for memory tool error reports.
+ * We read from /sys/kernel/tracing/instances/bootreceiver/trace_pipe (set up by the
+ * system), which will print an ftrace event when a memory corruption is detected in the
+ * kernel.
+ * When an error is detected, we run the dmesg shell command and process its output.
+ */
+ OnFileDescriptorEventListener traceCallback = new OnFileDescriptorEventListener() {
+ final int mBufferSize = 1024;
+ byte[] mTraceBuffer = new byte[mBufferSize];
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ /*
+ * Read from the tracing pipe set up to monitor the error_report_end events.
+ * When a tracing event occurs, the kernel writes a short (~100 bytes) line to the
+ * pipe, e.g.:
+ * ...-11210 [004] d..1 285.322307: error_report_end: [kfence] ffffff8938a05000
+ * The buffer size we use for reading should be enough to read the whole
+ * line, but to be on the safe side we keep reading until the buffer
+ * contains a '\n' character. In the unlikely case of a very buggy kernel
+ * the buffer may contain multiple tracing events that cannot be attributed
+ * to particular error reports. In that case the latest error report
+ * residing in dmesg is picked.
+ */
+ try {
+ int nbytes = Os.read(fd, mTraceBuffer, 0, mBufferSize);
+ if (nbytes > 0) {
+ String readStr = new String(mTraceBuffer);
+ if (readStr.indexOf("\n") == -1) {
+ return OnFileDescriptorEventListener.EVENT_INPUT;
+ }
+ processDmesg(context);
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Error processing dmesg output", e);
+ return 0; // Unregister the handler.
+ }
+ return OnFileDescriptorEventListener.EVENT_INPUT;
+ }
+ };
+
+ IoThread.get().getLooper().getQueue().addOnFileDescriptorEventListener(
+ tracefd, OnFileDescriptorEventListener.EVENT_INPUT, traceCallback);
+
+ }
+
+ /**
+ * Check whether it is safe to collect this dmesg line or not.
+ *
+ * We only consider lines belonging to KASAN or KFENCE reports, but those may still contain
+ * user information, namely the process name:
+ *
+ * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ...
+ *
+ * hardware information:
+ *
+ * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED>
+ *
+ * or register dump (in KASAN reports only):
+ *
+ * ... RIP: 0033:0x7f96443109da
+ * ... RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af
+ * ... RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da
+ *
+ * (on x86_64)
+ *
+ * ... pc : lpm_cpuidle_enter+0x258/0x384
+ * ... lr : lpm_cpuidle_enter+0x1d4/0x384
+ * ... sp : ffffff800820bea0
+ * ... x29: ffffff800820bea0 x28: ffffffc2305f3ce0
+ * ... ...
+ * ... x9 : 0000000000000001 x8 : 0000000000000000
+ * (on ARM64)
+ *
+ * We therefore omit the lines that contain "Comm:", "Hardware name:", or match the general
+ * purpose register regexp.
+ *
+ * @param line single line of `dmesg` output.
+ * @return updated line with sensitive data removed, or null if the line must be skipped.
+ */
+ public static String stripSensitiveData(String line) {
+ /*
+ * General purpose register names begin with "R" on x86_64 and "x" on ARM64. The letter is
+ * followed by two symbols (numbers, letters or spaces) and a colon, which is followed by a
+ * 16-digit hex number. The optional "_" prefix accounts for ORIG_RAX on x86.
+ */
+ final String registerRegex = "[ _][Rx]..: [0-9a-f]{16}";
+ final Pattern registerPattern = Pattern.compile(registerRegex);
+
+ final String corruptionRegex = "Detected corrupted memory at 0x[0-9a-f]+";
+ final Pattern corruptionPattern = Pattern.compile(corruptionRegex);
+
+ if (line.contains("Comm: ") || line.contains("Hardware name: ")) return null;
+ if (registerPattern.matcher(line).find()) return null;
+
+ Matcher cm = corruptionPattern.matcher(line);
+ if (cm.find()) return cm.group(0);
+ return line;
+ }
+
+ /*
+ * Search dmesg output for the last error report from KFENCE or KASAN and copy it to Dropbox.
+ *
+ * Example report printed by the kernel (redacted to fit into 100 column limit):
+ * [ 69.236673] [ T6006]c7 6006 =========================================================
+ * [ 69.245688] [ T6006]c7 6006 BUG: KFENCE: out-of-bounds in kfence_handle_page_fault
+ * [ 69.245688] [ T6006]c7 6006
+ * [ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 (...)
+ * [ 69.267102] [ T6006]c7 6006 kfence_handle_page_fault+0x1bc/0x208
+ * [ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c
+ * ...
+ * [ 69.355427] [ T6006]c7 6006 kfence-#2 [0xffffffca75c46f30-0xffffffca75c46fff, ...
+ * [ 69.366938] [ T6006]c7 6006 __d_alloc+0x3c/0x1b4
+ * [ 69.371946] [ T6006]c7 6006 d_alloc_parallel+0x48/0x538
+ * [ 69.377578] [ T6006]c7 6006 __lookup_slow+0x60/0x15c
+ * ...
+ * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ...
+ * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED>
+ * [ 69.567059] [ T6006]c7 6006 =========================================================
+ *
+ * We rely on the kernel printing task/CPU ID for every log line (CONFIG_PRINTK_CALLER=y).
+ * E.g. for the above report the task ID is T6006. Report lines may interleave with lines
+ * printed by other kernel tasks, which will have different task IDs, so in order to collect
+ * the report we:
+ * - find the next occurrence of the "BUG: " line in the kernel log, parse it to obtain the
+ * task ID and the tool name;
+ * - scan the rest of dmesg output and pick every line that has the same task ID, until we
+ * encounter a horizontal ruler, i.e.:
+ * [ 69.567059] [ T6006]c7 6006 ======================================================
+ * - add that line to the error report, unless it contains sensitive information (see
+ * logLinePotentiallySensitive())
+ * - repeat the above steps till the last report is found.
+ */
+ private void processDmesg(Context ctx) throws IOException {
+
+ /*
+ * Only SYSTEM_KASAN_ERROR_REPORT and SYSTEM_KFENCE_ERROR_REPORT are supported at the
+ * moment.
+ */
+ final String[] bugTypes = new String[] { "KASAN", "KFENCE" };
+ final String tsRegex = "^\\[[^]]+\\] ";
+ final String bugRegex =
+ tsRegex + "\\[([^]]+)\\].*BUG: (" + String.join("|", bugTypes) + "):";
+ final Pattern bugPattern = Pattern.compile(bugRegex);
+
+ Process p = new ProcessBuilder("/system/bin/timeout", "-k", "90s", "60s",
+ "dmesg").redirectErrorStream(true).start();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String line = null;
+ String task = null;
+ String tool = null;
+ String bugTitle = null;
+ Pattern reportPattern = null;
+ ArrayList<String> currentReport = null;
+ String lastReport = null;
+
+ while ((line = reader.readLine()) != null) {
+ if (currentReport == null) {
+ Matcher bm = bugPattern.matcher(line);
+ if (!bm.find()) continue;
+ task = bm.group(1);
+ tool = bm.group(2);
+ bugTitle = line;
+ currentReport = new ArrayList<String>();
+ currentReport.add(line);
+ String reportRegex = tsRegex + "\\[" + task + "\\].*";
+ reportPattern = Pattern.compile(reportRegex);
+ continue;
+ }
+ Matcher rm = reportPattern.matcher(line);
+ if (!rm.matches()) continue;
+ if ((line = stripSensitiveData(line)) == null) continue;
+ if (line.contains("================================")) {
+ lastReport = String.join("\n", currentReport);
+ currentReport = null;
+ continue;
+ }
+ currentReport.add(line);
+ }
+ if (lastReport == null) {
+ Slog.w(TAG, "Could not find report in dmesg.");
+ return;
+ }
+
+ // Avoid sending the same bug report twice.
+ if (bugTitle == sLastReportedBug) return;
+
+ final String reportTag = "SYSTEM_" + tool + "_ERROR_REPORT";
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ final String headers = getCurrentBootHeaders();
+ final String reportText = headers + lastReport;
+
+ addTextToDropBox(db, reportTag, reportText, "/dev/kmsg", LOG_SIZE);
+ sLastReportedBug = bugTitle;
}
private void removeOldUpdatePackages(Context context) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 542d527177a1..d7559873025d 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;
@@ -120,6 +120,7 @@ import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
@@ -141,10 +142,13 @@ import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnTransportInfo;
-import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
+import android.net.resolv.aidl.DnsHealthEventParcel;
+import android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener;
+import android.net.resolv.aidl.Nat64PrefixEventParcel;
+import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
@@ -155,7 +159,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
@@ -187,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;
@@ -201,7 +203,6 @@ import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.PermissionUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
-import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.KeepaliveTracker;
@@ -324,12 +325,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private INetworkManagementService mNMS;
@VisibleForTesting
protected IDnsResolver mDnsResolver;
@VisibleForTesting
protected INetd mNetd;
- private INetworkStatsService mStatsService;
+ private NetworkStatsManager mStatsManager;
private NetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
private final NetdCallback mNetdCallback;
@@ -1040,16 +1040,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- public ConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService) {
- this(context, netManager, statsService, getDnsResolver(context), new IpConnectivityLog(),
+ public ConnectivityService(Context context) {
+ this(context, getDnsResolver(context), new IpConnectivityLog(),
NetdService.getInstance(), new Dependencies());
}
@VisibleForTesting
- protected ConnectivityService(Context context, INetworkManagementService netManager,
- 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");
@@ -1095,8 +1093,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO: Consider making the timer customizable.
mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
- mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
- mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+ mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mPolicyManagerInternal = Objects.requireNonNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
@@ -1203,7 +1200,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
null /* broadcastPermission */, mHandler);
- mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNMS, mNetd);
+ mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNetd);
mNetdCallback = new NetdCallback();
try {
@@ -1215,9 +1212,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mSettingsObserver = new SettingsObserver(mContext, mHandler);
registerSettingsCallbacks();
- final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
- dataConnectionStats.startMonitoring();
-
mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager);
mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
@@ -1237,12 +1231,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDnsManager = new DnsManager(mContext, mDnsResolver);
registerPrivateDnsSettingsCallbacks();
- mNoServiceNetwork = new NetworkAgentInfo(null,
+ mNoServiceNetwork = new NetworkAgentInfo(null,
new Network(NO_SERVICE_NET_ID),
new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(), 0, mContext,
null, new NetworkAgentConfig(), this, null,
- null, null, 0, INVALID_UID,
+ null, 0, INVALID_UID,
mQosCallbackTracker);
}
@@ -1403,7 +1397,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
- private NetworkState getUnfilteredActiveNetworkState(int uid) {
+ private NetworkAgentInfo getNetworkAgentInfoForUid(int uid) {
NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
final Network[] networks = getVpnUnderlyingNetworks(uid);
@@ -1419,12 +1413,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai = null;
}
}
-
- if (nai != null) {
- return nai.getNetworkState();
- } else {
- return NetworkState.EMPTY;
- }
+ return nai;
}
/**
@@ -1477,24 +1466,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
- private void filterNetworkInfo(@NonNull NetworkInfo networkInfo,
- @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
- if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
- networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
- }
- networkInfo.setDetailedState(
- getLegacyLockdownState(networkInfo.getDetailedState()),
- "" /* reason */, null /* extraInfo */);
- }
-
/**
- * Apply any relevant filters to {@link NetworkState} for the given UID. For
+ * Apply any relevant filters to the specified {@link NetworkInfo} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
* on {@link #isNetworkWithCapabilitiesBlocked}.
*/
- private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
- if (state == null || state.networkInfo == null || state.linkProperties == null) return;
- filterNetworkInfo(state.networkInfo, state.networkCapabilities, uid, ignoreBlocked);
+ @NonNull
+ private NetworkInfo filterNetworkInfo(@NonNull NetworkInfo networkInfo, int type,
+ @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
+ 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
+ : filtered.getDetailedState();
+ filtered.setDetailedState(getLegacyLockdownState(state),
+ "" /* reason */, null /* extraInfo */);
+ return filtered;
+ }
+
+ private NetworkInfo getFilteredNetworkInfo(NetworkAgentInfo nai, int uid,
+ boolean ignoreBlocked) {
+ return filterNetworkInfo(nai.networkInfo, nai.networkInfo.getType(),
+ nai.networkCapabilities, uid, ignoreBlocked);
}
/**
@@ -1508,10 +1504,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
final int uid = mDeps.getCallingUid();
- final NetworkState state = getUnfilteredActiveNetworkState(uid);
- filterNetworkStateForUid(state, uid, false);
- maybeLogBlockedNetworkInfo(state.networkInfo, uid);
- return state.networkInfo;
+ final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
+ if (nai == null) return null;
+ final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, false);
+ maybeLogBlockedNetworkInfo(networkInfo, uid);
+ return networkInfo;
}
@Override
@@ -1546,30 +1543,37 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
PermissionUtils.enforceNetworkStackPermission(mContext);
- final NetworkState state = getUnfilteredActiveNetworkState(uid);
- filterNetworkStateForUid(state, uid, ignoreBlocked);
- return state.networkInfo;
+ final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
+ if (nai == null) return null;
+ return getFilteredNetworkInfo(nai, uid, ignoreBlocked);
+ }
+
+ /** Returns a NetworkInfo object for a network that doesn't exist. */
+ private NetworkInfo makeFakeNetworkInfo(int networkType, int uid) {
+ final NetworkInfo info = new NetworkInfo(networkType, 0 /* subtype */,
+ getNetworkTypeName(networkType), "" /* subtypeName */);
+ info.setIsAvailable(true);
+ // For compatibility with legacy code, return BLOCKED instead of DISCONNECTED when
+ // background data is restricted.
+ final NetworkCapabilities nc = new NetworkCapabilities(); // Metered.
+ final DetailedState state = isNetworkWithCapabilitiesBlocked(nc, uid, false)
+ ? DetailedState.BLOCKED
+ : DetailedState.DISCONNECTED;
+ info.setDetailedState(getLegacyLockdownState(state),
+ "" /* reason */, null /* extraInfo */);
+ return info;
}
- private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
+ private NetworkInfo getFilteredNetworkInfoForType(int networkType, int uid) {
if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
return null;
}
final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- final NetworkInfo info;
- final NetworkCapabilities nc;
- if (nai != null) {
- info = new NetworkInfo(nai.networkInfo);
- info.setType(networkType);
- nc = nai.networkCapabilities;
- } else {
- info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), "");
- info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
- info.setIsAvailable(true);
- nc = new NetworkCapabilities();
+ if (nai == null) {
+ return makeFakeNetworkInfo(networkType, uid);
}
- filterNetworkInfo(info, nc, uid, false);
- return info;
+ return filterNetworkInfo(nai.networkInfo, networkType, nai.networkCapabilities, uid,
+ false);
}
@Override
@@ -1579,27 +1583,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (getVpnUnderlyingNetworks(uid) != null) {
// A VPN is active, so we may need to return one of its underlying networks. This
// information is not available in LegacyTypeTracker, so we have to get it from
- // getUnfilteredActiveNetworkState.
- final NetworkState state = getUnfilteredActiveNetworkState(uid);
- if (state.networkInfo != null && state.networkInfo.getType() == networkType) {
- filterNetworkStateForUid(state, uid, false);
- return state.networkInfo;
+ // getNetworkAgentInfoForUid.
+ final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
+ if (nai == null) return null;
+ final NetworkInfo networkInfo = getFilteredNetworkInfo(nai, uid, false);
+ if (networkInfo.getType() == networkType) {
+ return networkInfo;
}
}
- return getFilteredNetworkInfo(networkType, uid);
+ return getFilteredNetworkInfoForType(networkType, uid);
}
@Override
public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
enforceAccessPermission();
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null) {
- final NetworkState state = nai.getNetworkState();
- filterNetworkStateForUid(state, uid, ignoreBlocked);
- return state.networkInfo;
- } else {
- return null;
- }
+ if (nai == null) return null;
+ return getFilteredNetworkInfo(nai, uid, ignoreBlocked);
}
@Override
@@ -1627,10 +1627,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
return null;
}
final int uid = mDeps.getCallingUid();
- if (!isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, false)) {
- return nai.network;
+ if (isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, false)) {
+ return null;
}
- return null;
+ return nai.network;
}
@Override
@@ -1719,9 +1719,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
public LinkProperties getActiveLinkProperties() {
enforceAccessPermission();
final int uid = mDeps.getCallingUid();
- NetworkState state = getUnfilteredActiveNetworkState(uid);
- if (state.linkProperties == null) return null;
- return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
+ NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
+ if (nai == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(nai.linkProperties,
Binder.getCallingPid(), uid);
}
@@ -1886,24 +1886,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ // TODO: Consider delete this function or turn it into a no-op method.
@Override
public NetworkState[] getAllNetworkState() {
// This contains IMSI details, so make sure the caller is privileged.
PermissionUtils.enforceNetworkStackPermission(mContext);
final ArrayList<NetworkState> result = new ArrayList<>();
+ for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshot()) {
+ // NetworkStateSnapshot doesn't contain NetworkInfo, so need to fetch it from the
+ // NetworkAgentInfo.
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(snapshot.network);
+ if (nai != null && nai.networkInfo.isConnected()) {
+ result.add(new NetworkState(new NetworkInfo(nai.networkInfo),
+ snapshot.linkProperties, snapshot.networkCapabilities, snapshot.network,
+ snapshot.subscriberId));
+ }
+ }
+ return result.toArray(new NetworkState[result.size()]);
+ }
+
+ @Override
+ @NonNull
+ public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+ // This contains IMSI details, so make sure the caller is privileged.
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+
+ final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- // TODO: Consider include SUSPENDED networks.
+ // TODO: Consider include SUSPENDED networks, which should be considered as
+ // temporary shortage of connectivity of a connected network.
if (nai != null && nai.networkInfo.isConnected()) {
- // TODO (b/73321673) : NetworkState contains a copy of the
+ // TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
// interfere ?
- result.add(nai.getNetworkState());
+ result.add(nai.getNetworkStateSnapshot());
}
}
- return result.toArray(new NetworkState[result.size()]);
+ return result;
}
@Override
@@ -2040,25 +2062,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
return true;
}
- private class NetdEventCallback extends INetdEventListener.Stub {
+ class DnsResolverUnsolicitedEventCallback extends
+ IDnsResolverUnsolicitedEventListener.Stub {
@Override
- public void onPrivateDnsValidationEvent(int netId, String ipAddress,
- String hostname, boolean validated) {
+ public void onPrivateDnsValidationEvent(final PrivateDnsValidationEventParcel event) {
try {
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
- new PrivateDnsValidationUpdate(netId,
- InetAddresses.parseNumericAddress(ipAddress),
- hostname, validated)));
+ new PrivateDnsValidationUpdate(event.netId,
+ InetAddresses.parseNumericAddress(event.ipAddress),
+ event.hostname, event.validation)));
} catch (IllegalArgumentException e) {
loge("Error parsing ip address in validation event");
}
}
@Override
- public void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
- String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
- NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ public void onDnsHealthEvent(final DnsHealthEventParcel event) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetId(event.netId);
// Netd event only allow registrants from system. Each NetworkMonitor thread is under
// the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
// event callback for certain nai. e.g. cellular. Register here to pass to
@@ -2067,34 +2088,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
// callback from each caller type. Need to re-factor NetdEventListenerService to allow
// multiple NetworkMonitor registrants.
if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
- nai.networkMonitor().notifyDnsResponse(returnCode);
+ nai.networkMonitor().notifyDnsResponse(event.healthResult);
}
}
@Override
- public void onNat64PrefixEvent(int netId, boolean added,
- String prefixString, int prefixLength) {
- mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength));
- }
-
- @Override
- public void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
- int uid) {
+ public void onNat64PrefixEvent(final Nat64PrefixEventParcel event) {
+ mHandler.post(() -> handleNat64PrefixEvent(event.netId, event.prefixOperation,
+ event.prefixAddress, event.prefixLength));
}
@Override
- public void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
- byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort,
- long timestampNs) {
- }
-
- @Override
- public void onTcpSocketStatsEvent(int[] networkIds, int[] sentPackets, int[] lostPackets,
- int[] rttsUs, int[] sentAckDiffsMs) {
- }
-
- @Override
- public int getInterfaceVersion() throws RemoteException {
+ public int getInterfaceVersion() {
return this.VERSION;
}
@@ -2102,16 +2107,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
public String getInterfaceHash() {
return this.HASH;
}
- };
+ }
@VisibleForTesting
- protected final INetdEventListener mNetdEventCallback = new NetdEventCallback();
+ protected final DnsResolverUnsolicitedEventCallback mResolverUnsolEventCallback =
+ new DnsResolverUnsolicitedEventCallback();
- private void registerNetdEventCallback() {
+ private void registerDnsResolverUnsolicitedEventListener() {
try {
- mDnsResolver.registerEventListener(mNetdEventCallback);
+ mDnsResolver.registerUnsolicitedEventListener(mResolverUnsolEventCallback);
} catch (Exception e) {
- loge("Error registering DnsResolver callback: " + e);
+ loge("Error registering DnsResolver unsolicited event callback: " + e);
}
}
@@ -2201,8 +2207,45 @@ public class ConnectivityService extends IConnectivityManager.Stub
"ConnectivityService");
}
+ /**
+ * Performs a strict and comprehensive check of whether a calling package is allowed to
+ * change the state of network, as the condition differs for pre-M, M+, and
+ * privileged/preinstalled apps. The caller is expected to have either the
+ * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
+ * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
+ * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
+ * permission and cannot be revoked. See http://b/23597341
+ *
+ * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
+ * of this app will be updated to the current time.
+ */
private void enforceChangePermission(String callingPkg, String callingAttributionTag) {
- ConnectivityManager.enforceChangePermission(mContext, callingPkg, callingAttributionTag);
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ if (callingPkg == null) {
+ throw new SecurityException("Calling package name is null.");
+ }
+
+ final AppOpsManager appOpsMgr = mContext.getSystemService(AppOpsManager.class);
+ final int uid = mDeps.getCallingUid();
+ final int mode = appOpsMgr.noteOpNoThrow(AppOpsManager.OPSTR_WRITE_SETTINGS, uid,
+ callingPkg, callingAttributionTag, null /* message */);
+
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
+ if ((mode == AppOpsManager.MODE_DEFAULT) && (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED)) {
+ return;
+ }
+
+ throw new SecurityException(callingPkg + " was not granted either of these permissions:"
+ + android.Manifest.permission.CHANGE_NETWORK_STATE + ","
+ + android.Manifest.permission.WRITE_SETTINGS + ".");
}
private void enforceSettingsPermission() {
@@ -2363,13 +2406,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 {
@@ -2405,7 +2441,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// to ensure the tracking will be initialized correctly.
mPermissionMonitor.startMonitoring();
mProxyTracker.loadGlobalProxy();
- registerNetdEventCallback();
+ registerDnsResolverUnsolicitedEventListener();
synchronized (this) {
mSystemReady = true;
@@ -3046,9 +3082,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {
- if (wasDefault) {
- mMetricsLog.logDefaultNetworkValidity(valid);
- }
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
nai.everValidated |= valid;
@@ -3172,16 +3205,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);
}
@@ -3268,8 +3301,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
@@ -3336,21 +3368,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
- private void handleNat64PrefixEvent(int netId, boolean added, String prefixString,
+ private void handleNat64PrefixEvent(int netId, int operation, String prefixAddress,
int prefixLength) {
NetworkAgentInfo nai = mNetworkForNetId.get(netId);
if (nai == null) return;
- log(String.format("NAT64 prefix %s on netId %d: %s/%d",
- (added ? "added" : "removed"), netId, prefixString, prefixLength));
+ log(String.format("NAT64 prefix changed on netId %d: operation=%d, %s/%d",
+ netId, operation, prefixAddress, prefixLength));
IpPrefix prefix = null;
- if (added) {
+ if (operation == IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED) {
try {
- prefix = new IpPrefix(InetAddresses.parseNumericAddress(prefixString),
+ prefix = new IpPrefix(InetAddresses.parseNumericAddress(prefixAddress),
prefixLength);
} catch (IllegalArgumentException e) {
- loge("Invalid NAT64 prefix " + prefixString + "/" + prefixLength);
+ loge("Invalid NAT64 prefix " + prefixAddress + "/" + prefixLength);
return;
}
}
@@ -3469,14 +3501,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
final boolean wasDefault = isDefaultNetwork(nai);
if (wasDefault) {
mDefaultInetConditionPublished = 0;
- // Log default network disconnection before required book-keeping.
- // Let rematchAllNetworksAndRequests() below record a new default network event
- // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
- // whose timestamps tell how long it takes to recover a default network.
- long now = SystemClock.elapsedRealtime();
- mMetricsLog.logDefaultNetworkEvent(null, 0, false,
- null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(),
- nai.linkProperties, nai.networkCapabilities);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -3818,7 +3842,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);
@@ -4131,13 +4172,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() {
@@ -4467,16 +4501,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;
@@ -5018,10 +5049,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onUserAdded(UserHandle user) {
mPermissionMonitor.onUserAdded(user);
+ if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
+ handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+ }
}
private void onUserRemoved(UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
+ if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
+ handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+ }
}
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -5234,11 +5271,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;
}
@@ -5285,6 +5331,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);
}
@@ -6030,7 +6078,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, mNMS, providerId, uid, mQosCallbackTracker);
+ this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc);
@@ -7112,27 +7160,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateTcpBufferSizes(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
-
- // Log 0 -> X and Y -> X default network transitions, where X is the new default.
- final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
- final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
- final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
- final LinkProperties lp = (newDefaultNetwork != null)
- ? newDefaultNetwork.linkProperties : null;
- final NetworkCapabilities nc = (newDefaultNetwork != null)
- ? newDefaultNetwork.networkCapabilities : null;
-
- final Network prevNetwork = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.network : null;
- final int prevScore = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.getCurrentScore() : 0;
- final LinkProperties prevLp = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.linkProperties : null;
- final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.networkCapabilities : null;
-
- mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
- prevNetwork, prevScore, prevLp, prevNc);
}
private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
@@ -7162,7 +7189,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 app default network", e);
}
}
@@ -7217,13 +7244,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;
@@ -7234,7 +7261,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");
}
@@ -7247,7 +7276,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
- if (!Build.IS_USER) {
+ if (Build.IS_DEBUGGABLE) {
// The code is never supposed to add two reassignments of the same request. Make
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
@@ -7294,14 +7323,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());
}
@@ -7318,12 +7347,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);
@@ -7921,7 +7951,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<>();
@@ -7935,7 +7966,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
defaultNetworks.add(nai.network);
}
}
- return defaultNetworks.toArray(new Network[0]);
+ return defaultNetworks;
}
/**
@@ -7952,8 +7983,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo();
try {
- mStatsService.forceUpdateIfaces(getDefaultNetworks(), getAllNetworkState(), activeIface,
- underlyingNetworkInfos);
+ final ArrayList<NetworkStateSnapshot> snapshots = new ArrayList<>();
+ // TODO: Directly use NetworkStateSnapshot when feasible.
+ for (final NetworkState state : getAllNetworkState()) {
+ final NetworkStateSnapshot snapshot = new NetworkStateSnapshot(state.network,
+ state.networkCapabilities, state.linkProperties, state.subscriberId,
+ state.legacyNetworkType);
+ snapshots.add(snapshot);
+ }
+ mStatsManager.notifyNetworkStatus(getDefaultNetworks(),
+ snapshots, activeIface, Arrays.asList(underlyingNetworkInfos));
} catch (Exception ignored) {
}
}
@@ -8186,7 +8225,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
TestNetworkService.enforceTestNetworkPermissions(mContext);
if (mTNS == null) {
- mTNS = new TestNetworkService(mContext, mNMS);
+ mTNS = new TestNetworkService(mContext);
}
return mTNS;
@@ -8272,24 +8311,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 +8384,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;
}
}
@@ -8661,9 +8695,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
private class NetdCallback extends BaseNetdUnsolicitedEventListener {
@Override
- public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel,
+ public void onInterfaceClassActivityChanged(boolean isActive, int transportType,
long timestampNs, int uid) {
- mNetworkActivityTracker.setAndReportNetworkActive(isActive, timerLabel, timestampNs);
+ mNetworkActivityTracker.setAndReportNetworkActive(isActive, transportType, timestampNs);
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String iface, boolean up) {
+ for (NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.clatd.interfaceLinkStateChanged(iface, up);
+ }
+ }
+
+ @Override
+ public void onInterfaceRemoved(String iface) {
+ for (NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.clatd.interfaceRemoved(iface);
+ }
}
}
@@ -8697,7 +8745,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
LegacyNetworkActivityTracker(@NonNull Context context, @NonNull Handler handler,
- @NonNull INetworkManagementService nms, @NonNull INetd netd) {
+ @NonNull INetd netd) {
mContext = context;
mNetd = netd;
mHandler = handler;
@@ -9019,23 +9067,27 @@ 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());
}
final ArraySet<NetworkRequestInfo> nris =
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
- updateDefaultNetworksForOemNetworkPreference(nris);
+ replaceDefaultNetworkRequestsForPreference(nris);
mOemNetworkPreferences = preference;
// TODO http://b/176496396 persist data to shared preferences.
if (null != listener) {
- listener.onComplete();
+ try {
+ listener.onComplete();
+ } catch (RemoteException e) {
+ loge("Can't send onComplete in handleSetOemNetworkPreference", e);
+ }
}
}
- private void updateDefaultNetworksForOemNetworkPreference(
+ private void replaceDefaultNetworkRequestsForPreference(
@NonNull final Set<NetworkRequestInfo> nris) {
// Pass in a defensive copy as this collection will be updated on remove.
handleRemoveNetworkRequests(new ArraySet<>(mDefaultNetworkRequests));
@@ -9047,10 +9099,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);
}
@@ -9121,6 +9173,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
return callbackRequestsToRegister;
}
+ private static void setNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
+ @NonNull final Set<UidRange> uids) {
+ final Set<UidRange> ranges = new ArraySet<>(uids);
+ for (final NetworkRequest req : requests) {
+ req.networkCapabilities.setUids(ranges);
+ }
+ }
+
/**
* Class used to generate {@link NetworkRequestInfo} based off of {@link OemNetworkPreferences}.
*/
@@ -9149,6 +9209,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull final OemNetworkPreferences preference) {
final SparseArray<Set<Integer>> uids = new SparseArray<>();
final PackageManager pm = mContext.getPackageManager();
+ final List<UserHandle> users =
+ mContext.getSystemService(UserManager.class).getUserHandles(true);
+ if (null == users || users.size() == 0) {
+ if (VDBG || DDBG) {
+ log("No users currently available for setting the OEM network preference.");
+ }
+ return uids;
+ }
for (final Map.Entry<String, Integer> entry :
preference.getNetworkPreferences().entrySet()) {
@OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue();
@@ -9157,7 +9225,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!uids.contains(pref)) {
uids.put(pref, new ArraySet<>());
}
- uids.get(pref).add(uid);
+ for (final UserHandle ui : users) {
+ // Add the rules for all users as this policy is device wide.
+ uids.get(pref).add(UserHandle.getUid(ui, uid));
+ }
} catch (PackageManager.NameNotFoundException e) {
// Although this may seem like an error scenario, it is ok that uninstalled
// packages are sent on a network preference as the system will watch for
@@ -9197,7 +9268,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
+ " called with invalid preference of " + preference);
}
- setOemNetworkRequestUids(requests, uids);
+ final ArraySet ranges = new ArraySet<Integer>();
+ for (final int uid : uids) {
+ ranges.add(new UidRange(uid, uid));
+ }
+ setNetworkRequestUids(requests, ranges);
return new NetworkRequestInfo(requests);
}
@@ -9230,16 +9305,5 @@ public class ConnectivityService extends IConnectivityManager.Stub
netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
return netCap;
}
-
- private void setOemNetworkRequestUids(@NonNull final List<NetworkRequest> requests,
- @NonNull final Set<Integer> uids) {
- final Set<UidRange> ranges = new ArraySet<>();
- for (final int uid : uids) {
- ranges.add(new UidRange(uid, uid));
- }
- for (final NetworkRequest req : requests) {
- req.networkCapabilities.setUids(ranges);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index 0779f7117d82..b9922087109f 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -20,9 +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.INetworkManagementService;
-import android.os.ServiceManager;
import android.util.Log;
/**
@@ -38,8 +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, getNetworkManagementService(),
- getNetworkStatsService());
+ mConnectivity = new ConnectivityService(context);
}
@Override
@@ -48,14 +44,4 @@ public final class ConnectivityServiceInitializer extends SystemService {
publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
}
-
- private INetworkManagementService getNetworkManagementService() {
- return INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
- }
-
- private INetworkStatsService getNetworkStatsService() {
- return INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- }
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 2906ceebca58..906702866889 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -839,23 +839,34 @@ public abstract class IntentResolver<F, R extends Object> {
}
};
+ // Helper method to copy some of the maps.
+ private static <E> void copyInto(ArrayMap<String, E[]> l, ArrayMap<String, E[]> r) {
+ final int end = r.size();
+ l.ensureCapacity(end);
+ for (int i = 0; i < end; i++) {
+ final E[] val = r.valueAt(i);
+ final String key = r.keyAt(i);
+ l.put(key, Arrays.copyOf(val, val.length));
+ }
+ }
+
// Make <this> a copy of <orig>. The presumption is that <this> is empty but all
// arrays are cleared out explicitly, just to be sure.
protected void copyFrom(IntentResolver orig) {
mFilters.clear();
mFilters.addAll(orig.mFilters);
mTypeToFilter.clear();
- mTypeToFilter.putAll(orig.mTypeToFilter);
+ copyInto(mTypeToFilter, orig.mTypeToFilter);
mBaseTypeToFilter.clear();
- mBaseTypeToFilter.putAll(orig.mBaseTypeToFilter);
+ copyInto(mBaseTypeToFilter, orig.mBaseTypeToFilter);
mWildTypeToFilter.clear();
- mWildTypeToFilter.putAll(orig.mWildTypeToFilter);
+ copyInto(mWildTypeToFilter, orig.mWildTypeToFilter);
mSchemeToFilter.clear();
- mSchemeToFilter.putAll(orig.mSchemeToFilter);
+ copyInto(mSchemeToFilter, orig.mSchemeToFilter);
mActionToFilter.clear();
- mActionToFilter.putAll(orig.mActionToFilter);
+ copyInto(mActionToFilter, orig.mActionToFilter);
mTypedActionToFilter.clear();
- mTypedActionToFilter.putAll(orig.mTypedActionToFilter);
+ copyInto(mTypedActionToFilter, orig.mTypedActionToFilter);
}
/**
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 44054088d937..10d6570929ed 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -44,7 +44,6 @@ import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENAB
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetdUnsolicitedEventListener;
import android.net.INetworkManagementEventObserver;
@@ -54,7 +53,6 @@ import android.net.InterfaceConfiguration;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.Network;
import android.net.NetworkPolicyManager;
import android.net.NetworkStack;
import android.net.NetworkStats;
@@ -974,19 +972,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void setDnsForwarders(Network network, String[] dns) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
-
- try {
- mNetdService.tetherDnsSet(netId, dns);
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public String[] getDnsForwarders() {
NetworkStack.checkNetworkStackPermission(mContext);
try {
@@ -1775,27 +1760,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
@Override
- public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
- NetworkStack.checkNetworkStackPermission(mContext);
-
- final LinkAddress la = routeInfo.getDestinationLinkAddress();
- final String ifName = routeInfo.getInterface();
- final String dst = la.toString();
- final String nextHop;
-
- if (routeInfo.hasGateway()) {
- nextHop = routeInfo.getGateway().getHostAddress();
- } else {
- nextHop = "";
- }
- try {
- mNetdService.networkAddLegacyRoute(netId, ifName, dst, nextHop, uid);
- } catch (RemoteException | ServiceSpecificException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
public void allowProtect(int uid) {
NetworkStack.checkNetworkStackPermission(mContext);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 233a50d417ad..27b648e53a38 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -24,6 +24,10 @@ import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -55,6 +59,7 @@ import android.app.AnrController;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
+import android.app.PendingIntent;
import android.app.admin.SecurityLog;
import android.app.usage.StorageStatsManager;
import android.content.BroadcastReceiver;
@@ -573,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.
@@ -943,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;
+ }
}
}
@@ -3371,6 +3408,54 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ /**
+ * Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission
+ * to launch the manageSpaceActivity of the App specified by packageName.
+ */
+ @Override
+ @Nullable
+ public PendingIntent getManageSpaceActivityIntent(
+ @NonNull String packageName, int requestCode) {
+ // Only Apps with MANAGE_EXTERNAL_STORAGE permission should be able to call this API.
+ enforcePermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE);
+
+ // We want to call the manageSpaceActivity as a SystemService and clear identity
+ // of the calling App
+ int originalUid = Binder.getCallingUidOrThrow();
+ long token = Binder.clearCallingIdentity();
+
+ try {
+ ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
+ UserHandle.getUserId(originalUid));
+ if (appInfo == null) {
+ throw new IllegalArgumentException(
+ "Invalid packageName");
+ }
+ if (appInfo.manageSpaceActivityName == null) {
+ Log.i(TAG, packageName + " doesn't have a manageSpaceActivity");
+ return null;
+ }
+ Context targetAppContext = mContext.createPackageContext(packageName, 0);
+
+ Intent intent = new Intent(Intent.ACTION_DEFAULT);
+ intent.setClassName(packageName,
+ appInfo.manageSpaceActivityName);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+
+ PendingIntent activity = PendingIntent.getActivity(targetAppContext, requestCode,
+ intent,
+ FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+ return activity;
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException(
+ "packageName not found");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
private boolean mMounted = false;
@@ -4637,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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 5a5f1a3f3723..978bd643f9b7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -72,6 +72,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -153,7 +154,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- boolean matchPhoneStateListenerEvent(int event) {
+ boolean matchTelephonyCallbackEvent(int event) {
return (callback != null) && (this.eventList.contains(event));
}
@@ -198,8 +199,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
public int getRegistrationLimit() {
return Binder.withCleanCallingIdentity(() ->
DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
- PhoneStateListener.FLAG_PER_PID_REGISTRATION_LIMIT,
- PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT));
+ TelephonyCallback.FLAG_PER_PID_REGISTRATION_LIMIT,
+ TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT));
}
/**
@@ -210,7 +211,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*/
public boolean isRegistrationLimitEnabledInPlatformCompat(int uid) {
return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
- PhoneStateListener.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
+ TelephonyCallback.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
}
}
@@ -332,37 +333,37 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
static {
REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>();
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+ TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
- REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
- REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
- PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+ TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
}
private boolean isLocationPermissionRequired(Set<Integer> events) {
- return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
- || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED)
- || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE)
- || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ return events.contains(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_CELL_INFO_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_REGISTRATION_FAILURE)
+ || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
}
private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
- return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
- || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
- || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
- || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
}
private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -375,14 +376,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) {
- return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)
- || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ return events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL)
+ || events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
}
private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
- return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
- || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
- || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+ || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
}
private static final int MSG_USER_SWITCHED = 1;
@@ -903,7 +904,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
}
if (notifyNow && validatePhoneId(phoneId)) {
- if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){
try {
if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
@@ -920,7 +921,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
int gsmSignalStrength = mSignalStrength[phoneId]
@@ -933,7 +934,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+ TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
try {
r.callback.onMessageWaitingIndicatorChanged(
mMessageWaiting[phoneId]);
@@ -942,7 +943,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+ TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
try {
r.callback.onCallForwardingIndicatorChanged(
mCallForwarding[phoneId]);
@@ -951,7 +952,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (validateEventAndUserLocked(
- r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+ r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
try {
if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
@@ -963,7 +964,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
try {
r.callback.onCallStateChanged(mCallState[phoneId],
getCallIncomingNumber(r, phoneId));
@@ -971,7 +972,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
mDataConnectionNetworkType[phoneId]);
@@ -979,14 +980,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) {
try {
r.callback.onDataActivity(mDataActivity[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
@@ -996,7 +997,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
updateReportSignalStrengthDecision(r.subId);
try {
if (mSignalStrength[phoneId] != null) {
@@ -1007,7 +1008,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (validateEventAndUserLocked(
- r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+ r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
@@ -1019,14 +1020,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
mCallPreciseDisconnectCause[phoneId]);
@@ -1034,7 +1035,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
} catch (RemoteException ex) {
@@ -1042,7 +1043,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
+ TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
try {
for (PreciseDataConnectionState pdcs
: mPreciseDataConnectionStates.get(phoneId).values()) {
@@ -1052,14 +1053,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
try {
r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
try {
r.callback.onVoiceActivationStateChanged(
mVoiceActivationState[phoneId]);
@@ -1067,21 +1068,21 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
try {
r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
try {
r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
try {
if (mTelephonyDisplayInfos[phoneId] != null) {
r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
@@ -1090,14 +1091,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) {
try {
r.callback.onPhoneCapabilityChanged(mPhoneCapability);
} catch (RemoteException ex) {
@@ -1105,35 +1106,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+ TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
try {
r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)) {
try {
r.callback.onRadioPowerStateChanged(mRadioPowerState);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) {
try {
r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
- if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) {
BarringInfo barringInfo = mBarringInfo.get(phoneId);
BarringInfo biNoLocation = barringInfo != null
? barringInfo.createLocationInfoSanitizedCopy() : null;
@@ -1147,7 +1148,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
+ TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
try {
r.callback.onPhysicalChannelConfigChanged(
mPhysicalChannelConfigs);
@@ -1156,7 +1157,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
+ TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) {
try {
r.callback.onDataEnabledChanged(
mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
@@ -1165,7 +1166,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (events.contains(
- PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
+ TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
try {
r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
} catch (RemoteException ex) {
@@ -1183,8 +1184,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
// If any of the system clients wants to always listen to signal strength,
// we need to set it on.
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
telephonyManager.createForSubscriptionId(subscriptionId)
.setAlwaysReportSignalStrength(true);
return;
@@ -1233,7 +1234,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
throw new IllegalStateException(errorMsg);
}
} else if (doesLimitApply && numRecordsForPid
- >= PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
+ >= TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
// Log the warning independently of the dynamically set limit -- apps shouldn't be
// doing this regardless of whether we're throwing them an exception for it.
Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible"
@@ -1284,8 +1285,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// Every time a client that is registrating to always receive the signal
// strength is removed from registry records, we need to check if
// the signal strength decision needs to update on its slot.
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
updateReportSignalStrengthDecision(r.subId);
}
return;
@@ -1305,7 +1306,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
&& (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
// Ensure the listener has read call log permission; if they do not return
@@ -1340,7 +1341,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallState[phoneId] = state;
mCallIncomingNumber[phoneId] = incomingNumber;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED)
&& (r.subId == subId)
&& (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
@@ -1382,8 +1383,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " state=" + state);
}
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
@@ -1444,8 +1445,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
try {
if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
- && r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+ && r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
@@ -1455,8 +1456,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callback.onVoiceActivationStateChanged(activationState);
}
if ((activationType == SIM_ACTIVATION_TYPE_DATA)
- && r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)
+ && r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
@@ -1495,11 +1496,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
- || r.matchPhoneStateListenerEvent(
- PhoneStateListener.
- EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
+ if ((r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
+ || r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
@@ -1512,8 +1512,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRemoveList.add(r.binder);
}
}
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
@@ -1559,8 +1559,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
}
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCarrierNetworkChange(active);
@@ -1592,7 +1592,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
if (validateEventAndUserLocked(
- r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)
+ r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)
&& idMatch(r.subId, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
@@ -1625,8 +1625,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mMessageWaiting[phoneId] = mwi;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
@@ -1652,8 +1652,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mUserMobileDataState[phoneId] = state;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onUserMobileDataStateChanged(state);
@@ -1691,8 +1691,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
&& idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
try {
r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
@@ -1723,8 +1723,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mCallForwarding[phoneId] = cfi;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
@@ -1752,8 +1752,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataActivity[phoneId] = state;
for (Record r : mRecords) {
// Notify by correct subId.
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onDataActivity(state);
@@ -1800,8 +1800,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log(str);
mLocalLog.log(str);
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
@@ -1825,8 +1825,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
.remove(key);
if (!Objects.equals(oldState, preciseState)) {
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(preciseState);
@@ -1872,7 +1872,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCellIdentity[phoneId] = cellIdentity;
for (Record r : mRecords) {
if (validateEventAndUserLocked(
- r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
+ r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
&& idMatch(r.subId, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
@@ -1925,8 +1925,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
@@ -1934,8 +1934,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRemoveList.add(r.binder);
}
}
- if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+ if (notifyCallAttributes && r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -1959,8 +1959,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallDisconnectCause[phoneId] = disconnectCause;
mCallPreciseDisconnectCause[phoneId] = preciseDisconnectCause;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener
- .LISTEN_CALL_DISCONNECT_CAUSES) && idMatch(r.subId, subId, phoneId)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
mCallPreciseDisconnectCause[phoneId]);
@@ -1983,8 +1984,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mImsReasonInfo.set(phoneId, imsReasonInfo);
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -2015,8 +2016,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mSrvccState[phoneId] = state;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -2044,8 +2045,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (VDBG) {
log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
}
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_OEM_HOOK_RAW))
+ if ((r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_OEM_HOOK_RAW))
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onOemHookRawEvent(rawData);
@@ -2072,8 +2073,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mPhoneCapability = capability;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) {
try {
r.callback.onPhoneCapabilityChanged(capability);
} catch (RemoteException ex) {
@@ -2097,8 +2098,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mActiveDataSubId = activeDataSubId;
synchronized (mRecords) {
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
try {
r.callback.onActiveDataSubIdChanged(activeDataSubId);
} catch (RemoteException ex) {
@@ -2124,8 +2125,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRadioPowerState = state;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onRadioPowerStateChanged(state);
@@ -2153,8 +2154,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mEmergencyNumberList = tm.getEmergencyNumberList();
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -2185,8 +2186,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
for (Record r : mRecords) {
// Send to all listeners regardless of subscription
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL)) {
try {
r.callback.onOutgoingEmergencyCall(emergencyNumber, subId);
} catch (RemoteException ex) {
@@ -2204,13 +2205,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyOutgoingEmergencySms()")) {
return;
}
+
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
for (Record r : mRecords) {
// Send to all listeners regardless of subscription
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS)) {
try {
r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
} catch (RemoteException ex) {
@@ -2239,8 +2241,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
callNetworkType, callQuality);
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -2270,8 +2272,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_REGISTRATION_FAILURE)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_REGISTRATION_FAILURE)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onRegistrationFailed(
@@ -2313,8 +2315,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy();
if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_BARRING_INFO_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_BARRING_INFO_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -2356,8 +2358,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -2386,7 +2388,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
* {@link TelephonyManager}.
*/
public void notifyDataEnabled(int phoneId, int subId, boolean enabled,
- @TelephonyManager.DataEnabledReason int reason) {
+ @TelephonyManager.DataEnabledReason int reason) {
if (!checkNotifyPermission("notifyDataEnabled()")) {
return;
}
@@ -2401,8 +2403,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled[phoneId] = enabled;
mDataEnabledReason[phoneId] = reason;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onDataEnabledChanged(enabled, reason);
@@ -2435,8 +2437,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mAllowedNetworkTypesList = allowedNetworkTypesList;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (VDBG) {
@@ -2817,7 +2819,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
}
- if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
+ if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
}
@@ -2847,7 +2849,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = UserHandle.getUserId(r.callerUid) == foregroundUser
- && r.matchPhoneStateListenerEvent(event);
+ && r.matchTelephonyCallbackEvent(event);
if (DBG | DBG_LOC) {
log("validateEventAndUserLocked: valid=" + valid
+ " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
@@ -2972,7 +2974,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return;
}
- if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) {
+ if ((events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED))) {
try {
if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" +
mServiceState[phoneId]);
@@ -2991,9 +2993,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
|| events.contains(
- PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
SignalStrength signalStrength = mSignalStrength[phoneId];
@@ -3008,7 +3010,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
int gsmSignalStrength = mSignalStrength[phoneId]
@@ -3025,7 +3027,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+ if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
if (DBG_LOC) {
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
@@ -3040,7 +3042,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
@@ -3052,7 +3054,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
@@ -3066,7 +3068,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId="
@@ -3079,7 +3081,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId="
@@ -3092,7 +3094,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+ if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
try {
if (DBG_LOC) {
log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = "
@@ -3108,7 +3110,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+ if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
if (DBG) {
log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState"
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 96f832d26816..55408ea61566 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -32,7 +32,6 @@ import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
-import android.net.NetworkStack;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
@@ -41,7 +40,6 @@ import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -51,6 +49,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
+import com.android.net.module.util.PermissionUtils;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -69,7 +68,6 @@ class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
@NonNull private final Context mContext;
- @NonNull private final INetworkManagementService mNMS;
@NonNull private final INetd mNetd;
@NonNull private final HandlerThread mHandlerThread;
@@ -82,14 +80,12 @@ class TestNetworkService extends ITestNetworkManager.Stub {
private static native int jniCreateTunTap(boolean isTun, @NonNull String iface);
@VisibleForTesting
- protected TestNetworkService(
- @NonNull Context context, @NonNull INetworkManagementService netManager) {
+ protected TestNetworkService(@NonNull Context context) {
mHandlerThread = new HandlerThread("TestNetworkServiceThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mContext = Objects.requireNonNull(context, "missing Context");
- mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
mCm = mContext.getSystemService(ConnectivityManager.class);
mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
@@ -324,7 +320,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
try {
final long token = Binder.clearCallingIdentity();
try {
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
NetdUtils.setInterfaceUp(mNetd, iface);
} finally {
Binder.restoreCallingIdentity(token);
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/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 9930eac5cbd5..73755231c3be 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -279,7 +279,7 @@ public class AccountManagerService
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mHandler = new MessageHandler(injector.getMessageHandlerLooper());
mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
- mAuthenticatorCache.setListener(this, null /* Handler */);
+ mAuthenticatorCache.setListener(this, mHandler);
sThis.set(this);
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 7cd494976c94..9e5d7e4c3325 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -50,7 +50,6 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
-import static android.os.PowerWhitelistManager.REASON_UNKNOWN;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
@@ -92,6 +91,7 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -104,7 +104,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -1178,14 +1177,16 @@ public class ActivityManagerService extends IActivityManager.Stub
final String tag;
final int type;
final @ReasonCode int reasonCode;
+ final int callingUid;
PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag,
- int type) {
+ int type, int callingUid) {
this.targetUid = targetUid;
this.duration = duration;
this.tag = tag;
this.type = type;
this.reasonCode = reasonCode;
+ this.callingUid = callingUid;
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1198,6 +1199,8 @@ public class ActivityManagerService extends IActivityManager.Stub
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE,
reasonCode);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.CALLING_UID,
+ callingUid);
proto.end(token);
}
}
@@ -2684,6 +2687,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
@@ -4963,7 +4971,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public List<ResolveInfo> queryIntentComponentsForIntentSender(
+ public ParceledListSlice<ResolveInfo> queryIntentComponentsForIntentSender(
IIntentSender pendingResult, int matchFlags) {
enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
"queryIntentComponentsForIntentSender()");
@@ -4981,15 +4989,15 @@ public class ActivityManagerService extends IActivityManager.Stub
final int userId = res.key.userId;
switch (res.key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
- return mContext.getPackageManager().queryIntentActivitiesAsUser(
- intent, matchFlags, userId);
+ return new ParceledListSlice<>(mContext.getPackageManager()
+ .queryIntentActivitiesAsUser(intent, matchFlags, userId));
case ActivityManager.INTENT_SENDER_SERVICE:
case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
- return mContext.getPackageManager().queryIntentServicesAsUser(
- intent, matchFlags, userId);
+ return new ParceledListSlice<>(mContext.getPackageManager()
+ .queryIntentServicesAsUser(intent, matchFlags, userId));
case ActivityManager.INTENT_SENDER_BROADCAST:
- return mContext.getPackageManager().queryBroadcastReceiversAsUser(
- intent, matchFlags, userId);
+ return new ParceledListSlice<>(mContext.getPackageManager()
+ .queryBroadcastReceiversAsUser(intent, matchFlags, userId));
default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT
throw new IllegalStateException("Unsupported intent sender type: " + res.key.type);
}
@@ -6099,6 +6107,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(
@@ -9222,6 +9234,8 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.print(ptw.type);
pw.print(" ");
pw.print(ptw.reasonCode);
+ pw.print(" ");
+ pw.print(ptw.callingUid);
}
}
}
@@ -10776,9 +10790,12 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.print(" unmapped + ");
pw.print(stringifyKBSize(ionPool));
pw.println(" pools)");
+ kernelUsed += ionUnmapped;
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
- // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
- kernelUsed += ionHeap;
+ // set on ION VMAs, however it might be included by the memtrack HAL.
+ // Replace memtrack HAL reported Graphics category with mapped dmabufs
+ ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GRAPHICS];
+ ss[INDEX_TOTAL_PSS] += dmabufMapped;
} else {
final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
if (totalExportedDmabuf >= 0) {
@@ -14501,7 +14518,8 @@ public class ActivityManagerService extends IActivityManager.Stub
String reason, int type, int callingUid) {
synchronized (mProcLock) {
mPendingTempAllowlist.put(targetUid,
- new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type));
+ new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type,
+ callingUid));
setUidTempAllowlistStateLSP(targetUid, true);
mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
@@ -14532,7 +14550,8 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i = 0; i < N; i++) {
PendingTempAllowlist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag);
+ ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag,
+ ptw.callingUid);
}
}
@@ -15179,8 +15198,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mFgsStartTempAllowList.add(changingUid, durationMs, reasonCode, reason,
callingUid);
}
- setAppIdTempAllowlistStateLSP(changingUid, adding);
}
+ setAppIdTempAllowlistStateLSP(changingUid, adding);
}
}
}
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/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index f8494d8a7c04..31ea14a73409 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1517,17 +1517,21 @@ public class AppProfiler {
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
final long ionPool = Debug.getIonPoolsSizeKb();
+ final long dmabufMapped = Debug.getDmabufMappedSizeKb();
if (ionHeap >= 0 && ionPool >= 0) {
+ final long ionUnmapped = ionHeap - dmabufMapped;
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
+ kernelUsed += ionUnmapped;
// Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
- // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
- kernelUsed += ionHeap;
+ // set on ION VMAs, however it might be included by the memtrack HAL.
+ // Replace memtrack HAL reported Graphics category with mapped dmabufs
+ totalPss -= totalMemtrackGraphics;
+ totalPss += dmabufMapped;
} else {
final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();
if (totalExportedDmabuf >= 0) {
- final long dmabufMapped = Debug.getDmabufMappedSizeKb();
final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;
memInfoBuilder.append("DMA-BUF: ");
memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf));
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 167c2b1ad66c..360e1b4e48a8 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);
}
@@ -346,6 +371,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
Watchdog.getInstance().addMonitor(this);
+
+ final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
+ dataConnectionStats.startMonitoring();
}
private final class LocalService extends BatteryStatsInternal {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 29061930cd84..81c4c8605fb4 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;
@@ -928,6 +930,8 @@ public final class BroadcastQueue {
} else if (r.intent.getData() != null) {
b.append(r.intent.getData());
}
+ b.append(",reason:");
+ b.append(reason);
if (DEBUG_BROADCAST) {
Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
+ " type=" + type + " : " + b.toString());
@@ -1634,6 +1638,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/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java
index fbd089c1f0ee..6e39a4c802d9 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/am/DataConnectionStats.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package com.android.server.am;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -34,11 +34,11 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.app.IBatteryStats;
-import com.android.server.am.BatteryStatsService;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
+/** Class for receiving data connection state to report to {@link BatteryStatsService}. */
public class DataConnectionStats extends BroadcastReceiver {
private static final String TAG = "DataConnectionStats";
private static final boolean DEBUG = false;
@@ -52,6 +52,7 @@ public class DataConnectionStats extends BroadcastReceiver {
private SignalStrength mSignalStrength;
private ServiceState mServiceState;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
+ private int mNrState = NetworkRegistrationInfo.NR_STATE_NONE;
public DataConnectionStats(Context context, Handler listenerHandler) {
mContext = context;
@@ -61,14 +62,14 @@ public class DataConnectionStats extends BroadcastReceiver {
new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler));
}
+ /** Start data connection state monitoring. */
public void startMonitoring() {
- TelephonyManager phone =
- (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ TelephonyManager phone = mContext.getSystemService(TelephonyManager.class);
phone.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
@@ -99,11 +100,13 @@ public class DataConnectionStats extends BroadcastReceiver {
: regInfo.getAccessNetworkTechnology();
// If the device is in NSA NR connection the networkType will report as LTE.
// For cell dwell rate metrics, this should report NR instead.
- if (regInfo != null && regInfo.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
+ if (mNrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
networkType = TelephonyManager.NETWORK_TYPE_NR;
}
- if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
- networkType, visible ? "" : "not "));
+ if (DEBUG) {
+ Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
+ networkType, visible ? "" : "not "));
+ }
try {
mBatteryStats.notePhoneDataConnectionState(networkType, visible,
mServiceState.getState());
@@ -112,7 +115,7 @@ public class DataConnectionStats extends BroadcastReceiver {
}
}
- private final void updateSimState(Intent intent) {
+ private void updateSimState(Intent intent) {
String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
mSimState = TelephonyManager.SIM_STATE_ABSENT;
@@ -171,6 +174,7 @@ public class DataConnectionStats extends BroadcastReceiver {
@Override
public void onServiceStateChanged(ServiceState state) {
mServiceState = state;
+ mNrState = state.getNrState();
notePhoneDataConnectionState();
}
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/apphibernation/AppHibernationManagerInternal.java b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java
new file mode 100644
index 000000000000..b0335fe404f4
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.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 com.android.server.apphibernation;
+
+/**
+ * App hibernation manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AppHibernationManagerInternal {
+
+ /**
+ * @see AppHibernationService#isHibernatingForUser
+ */
+ public abstract boolean isHibernatingForUser(String packageName, int userId);
+
+ /**
+ * @see AppHibernationService#setHibernatingForUser
+ */
+ public abstract void setHibernatingForUser(String packageName, int userId,
+ boolean isHibernating);
+
+ /**
+ * @see AppHibernationService#isHibernatingGlobally
+ */
+ public abstract boolean isHibernatingGlobally(String packageName);
+
+ /**
+ * @see AppHibernationService#setHibernatingGlobally
+ */
+ public abstract void setHibernatingGlobally(String packageName, boolean isHibernating);
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 32ae87898085..968cf5f1df91 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -59,6 +59,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import java.io.File;
@@ -134,6 +135,8 @@ public final class AppHibernationService extends SystemService {
intentFilter.addAction(ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ LocalServices.addService(AppHibernationManagerInternal.class, mLocalService);
}
@Override
@@ -545,6 +548,36 @@ public final class AppHibernationService extends SystemService {
}
}
+ private final AppHibernationManagerInternal mLocalService = new LocalService(this);
+
+ private static final class LocalService extends AppHibernationManagerInternal {
+ private final AppHibernationService mService;
+
+ LocalService(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public boolean isHibernatingForUser(String packageName, int userId) {
+ return mService.isHibernatingForUser(packageName, userId);
+ }
+
+ @Override
+ public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernatingForUser(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ mService.setHibernatingGlobally(packageName, isHibernating);
+ }
+
+ @Override
+ public boolean isHibernatingGlobally(String packageName) {
+ return mService.isHibernatingGlobally(packageName);
+ }
+ }
+
private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 44dcc205a9d0..4a12ff6932de 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -843,11 +843,12 @@ 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);
+ tag, uidState, flags, accessTime);
}
/**
@@ -951,9 +952,10 @@ public class AppOpsService extends IAppOpsService.Stub {
mInProgressEvents = new ArrayMap<>(1);
}
+ long startTime = System.currentTimeMillis();
InProgressStartOpEvent event = mInProgressEvents.get(clientId);
if (event == null) {
- event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(),
+ event = mInProgressStartOpEventPool.acquire(startTime,
SystemClock.elapsedRealtime(), clientId,
PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
@@ -967,7 +969,7 @@ public class AppOpsService extends IAppOpsService.Stub {
event.numUnfinishedStarts++;
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags);
+ tag, uidState, flags, startTime);
}
/**
@@ -1004,14 +1006,16 @@ 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);
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
parent.packageName, tag, event.getUidState(),
- event.getFlags(), finishedEvent.getDuration());
+ event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
mInProgressStartOpEventPool.release(event);
@@ -2087,8 +2091,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 +2124,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 +2144,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 +4763,7 @@ public class AppOpsService extends IAppOpsService.Stub {
mFile.failWrite(stream);
}
}
+ mHistoricalRegistry.writeAndClearDiscreteHistory();
}
static class Shell extends ShellCommand {
@@ -6114,7 +6119,7 @@ public class AppOpsService extends IAppOpsService.Stub {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS,
"clearHistory");
// Must not hold the appops lock
- mHistoricalRegistry.clearHistory();
+ mHistoricalRegistry.clearAllHistory();
}
@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..a99d90883f87
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -0,0 +1,622 @@
+/*
+ * 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 (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime
+ && accessDuration > -1) {
+ // existing event with updated duration
+ attributedOps.remove(i);
+ break;
+ }
+ 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..0fcf5ee35e11 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";
+ private 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,59 +417,68 @@ 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,
- @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
+ long accessTime) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -461,6 +488,9 @@ final class HistoricalRegistry {
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
attributionTag, uidState, flags, 1);
+
+ mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
+ flags, uidState, accessTime, -1);
}
}
}
@@ -482,7 +512,7 @@ final class HistoricalRegistry {
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long increment) {
+ long eventStartTime, long increment) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -492,6 +522,8 @@ final class HistoricalRegistry {
getUpdatedPendingHistoricalOpsMLocked(
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
+ mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
+ flags, uidState, increment, eventStartTime);
}
}
}
@@ -536,7 +568,7 @@ final class HistoricalRegistry {
return;
}
final List<HistoricalOps> history = mPersistence.readHistoryDLocked();
- clearHistory();
+ clearHistoricalRegistry();
if (history != null) {
final int historySize = history.size();
for (int i = 0; i < historySize; i++) {
@@ -605,7 +637,16 @@ final class HistoricalRegistry {
}
}
- void clearHistory() {
+ void writeAndClearDiscreteHistory() {
+ mDiscreteRegistry.writeAndClearAccessHistory();
+ }
+
+ void clearAllHistory() {
+ clearHistoricalRegistry();
+ mDiscreteRegistry.clearHistory();
+ }
+
+ void clearHistoricalRegistry() {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
if (!isPersistenceInitializedMLocked()) {
@@ -692,6 +733,7 @@ final class HistoricalRegistry {
}
persistPendingHistory(pendingWrites);
}
+ mDiscreteRegistry.writeAndClearAccessHistory();
}
private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f5b94177a2d9..8363c9d203d5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6958,7 +6958,10 @@ public class AudioService extends IAudioService.Stub
private void onSetVolumeIndexOnDevice(@NonNull DeviceVolumeUpdate update) {
final VolumeStreamState streamState = mStreamStates[update.mStreamType];
if (update.hasVolumeIndex()) {
- final int index = update.getVolumeIndex();
+ int index = update.getVolumeIndex();
+ if (!checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) {
+ index = safeMediaVolumeIndex(update.mDevice);
+ }
streamState.setIndex(index, update.mDevice, update.mCaller,
// trusted as index is always validated before message is posted
true /*hasModifyAudioSettings*/);
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
new file mode 100644
index 000000000000..9f0a2baea91d
--- /dev/null
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -0,0 +1,245 @@
+/*
+ * 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.audio;
+
+import android.annotation.NonNull;
+import android.media.AudioAttributes;
+import android.media.AudioPlaybackConfiguration;
+import android.media.VolumeShaper;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Class to handle fading out players
+ */
+public final class FadeOutManager {
+
+ public static final String TAG = "AudioService.FadeOutManager";
+
+ /*package*/ static final long FADE_OUT_DURATION_MS = 2500;
+
+ private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
+
+ private static final VolumeShaper.Configuration FADEOUT_VSHAPE =
+ new VolumeShaper.Configuration.Builder()
+ .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID)
+ .setCurve(new float[]{0.f, 1.0f} /* times */,
+ new float[]{1.f, 0.0f} /* volumes */)
+ .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
+ .setDuration(FADE_OUT_DURATION_MS)
+ .build();
+ private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
+ new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
+ .createIfNeeded()
+ .build();
+
+ private static final int[] UNFADEABLE_PLAYER_TYPES = {
+ AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
+ AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL,
+ };
+
+ private static final int[] UNFADEABLE_CONTENT_TYPES = {
+ AudioAttributes.CONTENT_TYPE_SPEECH,
+ };
+
+ private static final int[] FADEABLE_USAGES = {
+ AudioAttributes.USAGE_GAME,
+ AudioAttributes.USAGE_MEDIA,
+ };
+
+ // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
+ private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
+ new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
+
+ /**
+ * Evaluates whether the player associated with this configuration can and should be faded out
+ * @param apc the configuration of the player
+ * @return true if player type and AudioAttributes are compatible with fade out
+ */
+ static boolean canBeFadedOut(@NonNull AudioPlaybackConfiguration apc) {
+ if (ArrayUtils.contains(UNFADEABLE_PLAYER_TYPES, apc.getPlayerType())) {
+ if (DEBUG) { Log.i(TAG, "not fading: player type:" + apc.getPlayerType()); }
+ return false;
+ }
+ if (ArrayUtils.contains(UNFADEABLE_CONTENT_TYPES,
+ apc.getAudioAttributes().getContentType())) {
+ if (DEBUG) {
+ Log.i(TAG, "not fading: content type:"
+ + apc.getAudioAttributes().getContentType());
+ }
+ return false;
+ }
+ if (!ArrayUtils.contains(FADEABLE_USAGES, apc.getAudioAttributes().getUsage())) {
+ if (DEBUG) {
+ Log.i(TAG, "not fading: usage:" + apc.getAudioAttributes().getUsage());
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Map of uid (key) to faded out apps (value)
+ */
+ private final HashMap<Integer, FadedOutApp> mFadedApps = new HashMap<Integer, FadedOutApp>();
+
+ synchronized void fadeOutUid(int uid, ArrayList<AudioPlaybackConfiguration> players) {
+ Log.i(TAG, "fadeOutUid() uid:" + uid);
+ if (!mFadedApps.containsKey(uid)) {
+ mFadedApps.put(uid, new FadedOutApp(uid));
+ }
+ final FadedOutApp fa = mFadedApps.get(uid);
+ for (AudioPlaybackConfiguration apc : players) {
+ fa.addFade(apc, false /*skipRamp*/);
+ }
+ }
+
+ synchronized void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+ Log.i(TAG, "unfadeOutUid() uid:" + uid);
+ final FadedOutApp fa = mFadedApps.remove(uid);
+ if (fa == null) {
+ return;
+ }
+ fa.removeUnfadeAll(players);
+ }
+
+ synchronized void forgetUid(int uid) {
+ //Log.v(TAG, "forget() uid:" + uid);
+ //mFadedApps.remove(uid);
+ // TODO unfade all players later in case they are reused or the app continued to play
+ }
+
+ // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ // see {@link PlaybackActivityMonitor#playerEvent}
+ synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
+ if (DEBUG) {
+ Log.v(TAG, "checkFade() player piid:"
+ + apc.getPlayerInterfaceId() + " uid:" + apc.getClientUid());
+ }
+ final FadedOutApp fa = mFadedApps.get(apc.getClientUid());
+ if (fa == null) {
+ return;
+ }
+ fa.addFade(apc, true);
+ }
+
+ /**
+ * Remove the player from the list of faded out players because it has been released
+ * @param apc the released player
+ */
+ synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ final int uid = apc.getClientUid();
+ if (DEBUG) {
+ Log.v(TAG, "removedReleased() player piid: "
+ + apc.getPlayerInterfaceId() + " uid:" + uid);
+ }
+ final FadedOutApp fa = mFadedApps.get(uid);
+ if (fa == null) {
+ return;
+ }
+ fa.removeReleased(apc);
+ }
+
+ synchronized void dump(PrintWriter pw) {
+ for (FadedOutApp da : mFadedApps.values()) {
+ da.dump(pw);
+ }
+ }
+
+ //=========================================================================
+ /**
+ * Class to group players from a common app, that are faded out.
+ */
+ private static final class FadedOutApp {
+ private final int mUid;
+ private final ArrayList<Integer> mFadedPlayers = new ArrayList<Integer>();
+
+ FadedOutApp(int uid) {
+ mUid = uid;
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("\t uid:" + mUid + " piids:");
+ for (int piid : mFadedPlayers) {
+ pw.print(" " + piid);
+ }
+ pw.println("");
+ }
+
+ /**
+ * Add this player to the list of faded out players and apply the fade
+ * @param apc a config that satisfies
+ * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+ * @param skipRamp true if the player should be directly into the end of ramp state.
+ * This value would for instance be false when adding players at the start of a fade.
+ */
+ void addFade(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ final int piid = new Integer(apc.getPlayerInterfaceId());
+ if (mFadedPlayers.contains(piid)) {
+ if (DEBUG) {
+ Log.v(TAG, "player piid:" + piid + " already faded out");
+ }
+ return;
+ }
+ try {
+ PlaybackActivityMonitor.sEventLogger.log(
+ (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp)).printLog(TAG));
+ apc.getPlayerProxy().applyVolumeShaper(
+ FADEOUT_VSHAPE,
+ skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+ mFadedPlayers.add(piid);
+ } catch (Exception e) {
+ Log.e(TAG, "Error fading out player piid:" + piid
+ + " uid:" + apc.getClientUid(), e);
+ }
+ }
+
+ void removeUnfadeAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+ for (int piid : mFadedPlayers) {
+ final AudioPlaybackConfiguration apc = players.get(piid);
+ if (apc != null) {
+ try {
+ PlaybackActivityMonitor.sEventLogger.log(
+ (new AudioEventLogger.StringEvent("unfading out piid:"
+ + piid)).printLog(TAG));
+ apc.getPlayerProxy().applyVolumeShaper(
+ FADEOUT_VSHAPE,
+ VolumeShaper.Operation.REVERSE);
+ } catch (Exception e) {
+ Log.e(TAG, "Error unfading out player piid:" + piid + " uid:" + mUid, e);
+ }
+ } else {
+ // this piid was in the list of faded players, but wasn't found
+ if (DEBUG) {
+ Log.v(TAG, "Error unfading out player piid:" + piid
+ + ", player not found for uid " + mUid);
+ }
+ }
+ }
+ mFadedPlayers.clear();
+ }
+
+ void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+ mFadedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index b6d647289ed8..cc60fe1200b1 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -70,6 +70,11 @@ public class FocusRequester {
*/
private boolean mFocusLossWasNotified;
/**
+ * whether this focus owner has already lost focus, but is being faded out until focus loss
+ * dispatch occurs. It's in "limbo" mode: has lost focus but not released yet until notified
+ */
+ boolean mFocusLossFadeLimbo;
+ /**
* the audio attributes associated with the focus request
*/
private final @NonNull AudioAttributes mAttributes;
@@ -102,6 +107,7 @@ public class FocusRequester {
mGrantFlags = grantFlags;
mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
mFocusLossWasNotified = true;
+ mFocusLossFadeLimbo = false;
mFocusController = ctlr;
mSdkTarget = sdk;
}
@@ -115,6 +121,7 @@ public class FocusRequester {
mFocusGainRequest = afi.getGainRequest();
mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
mFocusLossWasNotified = true;
+ mFocusLossFadeLimbo = false;
mGrantFlags = afi.getFlags();
mSdkTarget = afi.getSdkTarget();
@@ -132,6 +139,13 @@ public class FocusRequester {
return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
}
+ /**
+ * @return true if the focus requester is scheduled to receive a focus loss
+ */
+ boolean isInFocusLossLimbo() {
+ return mFocusLossFadeLimbo;
+ }
+
boolean hasSameBinder(IBinder ib) {
return (mSourceRef != null) && mSourceRef.equals(ib);
}
@@ -231,11 +245,21 @@ public class FocusRequester {
+ " -- flags: " + flagsToString(mGrantFlags)
+ " -- loss: " + focusLossToString()
+ " -- notified: " + mFocusLossWasNotified
+ + " -- limbo" + mFocusLossFadeLimbo
+ " -- uid: " + mCallingUid
+ " -- attr: " + mAttributes
+ " -- sdk:" + mSdkTarget);
}
+ /**
+ * Clear all references, except for instances in "loss limbo" due to the current fade out
+ * for which there will be an attempt to be clear after the loss has been notified
+ */
+ void maybeRelease() {
+ if (!mFocusLossFadeLimbo) {
+ release();
+ }
+ }
void release() {
final IBinder srcRef = mSourceRef;
@@ -315,6 +339,7 @@ public class FocusRequester {
void handleFocusGain(int focusGain) {
try {
mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
+ mFocusLossFadeLimbo = false;
mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
final IAudioFocusDispatcher fd = mFocusDispatcher;
@@ -327,7 +352,7 @@ public class FocusRequester {
fd.dispatchAudioFocusChange(focusGain, mClientId);
}
}
- mFocusController.unduckPlayers(this);
+ mFocusController.restoreVShapedPlayers(this);
} catch (android.os.RemoteException e) {
Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
}
@@ -336,7 +361,7 @@ public class FocusRequester {
@GuardedBy("MediaFocusControl.mAudioFocusLock")
void handleFocusGainFromRequest(int focusRequestResult) {
if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mFocusController.unduckPlayers(this);
+ mFocusController.restoreVShapedPlayers(this);
}
}
@@ -375,7 +400,7 @@ public class FocusRequester {
if (handled) {
if (DEBUG) {
Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
- + " to " + mClientId + ", ducking implemented by framework");
+ + " to " + mClientId + ", response handled by framework");
}
mFocusController.notifyExtPolicyFocusLoss_syncAf(
toAudioFocusInfo(), false /* wasDispatched */);
@@ -435,8 +460,27 @@ public class FocusRequester {
return false;
}
- return mFocusController.duckPlayers(frWinner, this, forceDuck);
+ return mFocusController.duckPlayers(frWinner, /*loser*/ this, forceDuck);
+ }
+
+ if (focusLoss == AudioManager.AUDIOFOCUS_LOSS) {
+ if (!MediaFocusControl.ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
+ return false;
+ }
+
+ // candidate for fade-out before a receiving a loss
+ boolean playersAreFaded = mFocusController.fadeOutPlayers(frWinner, /* loser */ this);
+ if (playersAreFaded) {
+ // active players are being faded out, delay the dispatch of focus loss
+ // mark this instance as being faded so it's not released yet as the focus loss
+ // will be dispatched later, it is now in limbo mode
+ mFocusLossFadeLimbo = true;
+ mFocusController.postDelayedLossAfterFade(this,
+ FadeOutManager.FADE_OUT_DURATION_MS);
+ return true;
+ }
}
+
return false;
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index b1633b01df90..1dcfdaec44ec 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -30,7 +30,10 @@ import android.media.MediaMetrics;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Message;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
@@ -80,6 +83,12 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
*/
static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
+ /**
+ * set to true so the framework enforces fading out apps that lose audio focus in a
+ * non-transient way.
+ */
+ static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true;
+
private final Context mContext;
private final AppOpsManager mAppOps;
private PlayerFocusEnforcer mFocusEnforcer; // never null
@@ -98,6 +107,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
final ContentResolver cr = mContext.getContentResolver();
mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
+ initFocusThreading();
}
protected void dump(PrintWriter pw) {
@@ -119,8 +129,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
@Override
- public void unduckPlayers(@NonNull FocusRequester winner) {
- mFocusEnforcer.unduckPlayers(winner);
+ public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
+ mFocusEnforcer.restoreVShapedPlayers(winner);
}
@Override
@@ -133,6 +143,16 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
mFocusEnforcer.unmutePlayersForCall();
}
+ @Override
+ public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
+ return mFocusEnforcer.fadeOutPlayers(winner, loser);
+ }
+
+ @Override
+ public void forgetUid(int uid) {
+ mFocusEnforcer.forgetUid(uid);
+ }
+
//==========================================================================================
// AudioFocus
//==========================================================================================
@@ -294,7 +314,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
{
//Log.i(TAG, " removeFocusStackEntry() removing top of stack");
FocusRequester fr = mFocusStack.pop();
- fr.release();
+ fr.maybeRelease();
if (notifyFocusFollowers) {
abandonSource = fr.toAudioFocusInfo();
}
@@ -318,7 +338,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
abandonSource = fr.toAudioFocusInfo();
}
// stack entry not used anymore, clear references
- fr.release();
+ fr.maybeRelease();
}
}
}
@@ -1134,4 +1154,57 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
pw.println("------------------------------");
}
}
+
+ //=================================================================
+ // Async focus events
+ void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) {
+ if (DEBUG) {
+ Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
+ }
+ mFocusHandler.sendMessageDelayed(
+ mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
+ FadeOutManager.FADE_OUT_DURATION_MS);
+ }
+ //=================================================================
+ // Message handling
+ private Handler mFocusHandler;
+ private HandlerThread mFocusThread;
+
+ /**
+ * dispatch a focus loss after an app has been faded out. Focus loser is to be released
+ * after dispatch as it has already left the stack
+ * args:
+ * msg.obj: the audio focus loser
+ * type:FocusRequester
+ */
+ private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
+
+ private void initFocusThreading() {
+ mFocusThread = new HandlerThread(TAG);
+ mFocusThread.start();
+ mFocusHandler = new Handler(mFocusThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_L_FOCUS_LOSS_AFTER_FADE:
+ if (DEBUG) {
+ Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser="
+ + ((FocusRequester) msg.obj).getPackageName());
+ }
+ synchronized (mAudioFocusLock) {
+ final FocusRequester loser = (FocusRequester) msg.obj;
+ if (loser.isInFocusLossLimbo()) {
+ loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+ loser.release();
+ mFocusEnforcer.forgetUid(loser.getClientUid());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ }
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 8af1b5be1517..47c91e6c23dd 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -51,8 +51,9 @@ public final class PlaybackActivityMonitor
public static final String TAG = "AudioService.PlaybackActivityMonitor";
- private static final boolean DEBUG = false;
- private static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
+ /*package*/ static final boolean DEBUG = false;
+ /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
+ /*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2;
private static final VolumeShaper.Configuration DUCK_VSHAPE =
new VolumeShaper.Configuration.Builder()
@@ -298,6 +299,7 @@ public final class PlaybackActivityMonitor
}
if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
mDuckingManager.checkDuck(apc);
+ mFadingManager.checkFade(apc);
}
}
if (change) {
@@ -320,6 +322,7 @@ public final class PlaybackActivityMonitor
"releasing player piid:" + piid));
mPlayers.remove(new Integer(piid));
mDuckingManager.removeReleased(apc);
+ mFadingManager.removeReleased(apc);
checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
@@ -442,6 +445,9 @@ public final class PlaybackActivityMonitor
// ducked players
pw.println("\n ducked players piids:");
mDuckingManager.dump(pw);
+ // faded out players
+ pw.println("\n faded out players piids:");
+ mFadingManager.dump(pw);
// players muted due to the device ringing or being in a call
pw.print("\n muted player piids:");
for (int piid : mMutedPlayers) {
@@ -606,10 +612,11 @@ public final class PlaybackActivityMonitor
}
@Override
- public void unduckPlayers(@NonNull FocusRequester winner) {
+ public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
synchronized (mPlayerLock) {
mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
+ mFadingManager.unfadeOutUid(winner.getClientUid(), mPlayers);
}
}
@@ -678,6 +685,67 @@ public final class PlaybackActivityMonitor
}
}
+ private final FadeOutManager mFadingManager = new FadeOutManager();
+
+ /**
+ *
+ * @param winner the new non-transient focus owner
+ * @param loser the previous focus owner
+ * @return true if there are players being faded out
+ */
+ @Override
+ public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
+ if (DEBUG) {
+ Log.v(TAG, "fadeOutPlayers: winner=" + winner.getPackageName()
+ + " loser=" + loser.getPackageName());
+ }
+ boolean loserHasActivePlayers = false;
+
+ // find which players to fade out
+ synchronized (mPlayerLock) {
+ if (mPlayers.isEmpty()) {
+ return false;
+ }
+ // check if this UID needs to be faded out (return false if not), and gather list of
+ // eligible players to fade out
+ final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
+ final ArrayList<AudioPlaybackConfiguration> apcsToFadeOut =
+ new ArrayList<AudioPlaybackConfiguration>();
+ while (apcIterator.hasNext()) {
+ final AudioPlaybackConfiguration apc = apcIterator.next();
+ if (!winner.hasSameUid(apc.getClientUid())
+ && loser.hasSameUid(apc.getClientUid())
+ && apc.getPlayerState()
+ == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ if (!FadeOutManager.canBeFadedOut(apc)) {
+ // the player is not eligible to be faded out, bail
+ Log.v(TAG, "not fading out player " + apc.getPlayerInterfaceId()
+ + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
+ + " type:"
+ + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
+ apc.getPlayerType())
+ + " attr:" + apc.getAudioAttributes());
+ return false;
+ }
+ loserHasActivePlayers = true;
+ apcsToFadeOut.add(apc);
+ }
+ }
+ //###
+ //mDuckingManager.duckUid(loser.getClientUid(), apcsToFadeOut);
+ if (loserHasActivePlayers) {
+ mFadingManager.fadeOutUid(loser.getClientUid(), apcsToFadeOut);
+ }
+ }
+
+ return loserHasActivePlayers;
+ }
+
+ @Override
+ public void forgetUid(int uid) {
+ mFadingManager.forgetUid(uid);
+ }
+
//=================================================================
// Track playback activity listeners
@@ -964,13 +1032,15 @@ public final class PlaybackActivityMonitor
}
}
- private static final class DuckEvent extends AudioEventLogger.Event {
+ private abstract static class VolumeShaperEvent extends AudioEventLogger.Event {
private final int mPlayerIId;
private final boolean mSkipRamp;
private final int mClientUid;
private final int mClientPid;
- DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ abstract String getVSAction();
+
+ VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
mPlayerIId = apc.getPlayerInterfaceId();
mSkipRamp = skipRamp;
mClientUid = apc.getClientUid();
@@ -979,12 +1049,34 @@ public final class PlaybackActivityMonitor
@Override
public String eventToString() {
- return new StringBuilder("ducking player piid:").append(mPlayerIId)
+ return new StringBuilder(getVSAction()).append(" player piid:").append(mPlayerIId)
.append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
.append(" skip ramp:").append(mSkipRamp).toString();
}
}
+ static final class DuckEvent extends VolumeShaperEvent {
+ @Override
+ String getVSAction() {
+ return "ducking";
+ }
+
+ DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ super(apc, skipRamp);
+ }
+ }
+
+ static final class FadeOutEvent extends VolumeShaperEvent {
+ @Override
+ String getVSAction() {
+ return "fading out";
+ }
+
+ FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ super(apc, skipRamp);
+ }
+ }
+
private static final class AudioAttrEvent extends AudioEventLogger.Event {
private final int mPlayerIId;
private final AudioAttributes mPlayerAttr;
@@ -1000,6 +1092,6 @@ public final class PlaybackActivityMonitor
}
}
- private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
+ static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
"playback activity as reported through PlayerBase");
}
diff --git a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
index 89e7b7828b15..fb72ac282e8d 100644
--- a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
+++ b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
@@ -31,11 +31,12 @@ public interface PlayerFocusEnforcer {
boolean forceDuck);
/**
- * Unduck the players that had been ducked with
- * {@link #duckPlayers(FocusRequester, FocusRequester, boolean)}
+ * Restore the initial state of any players that had had a volume ramp applied as the result
+ * of a duck or fade out through {@link #duckPlayers(FocusRequester, FocusRequester, boolean)}
+ * or {@link #fadeOutPlayers(FocusRequester, FocusRequester)}
* @param winner
*/
- void unduckPlayers(@NonNull FocusRequester winner);
+ void restoreVShapedPlayers(@NonNull FocusRequester winner);
/**
* Mute players at the beginning of a call
@@ -47,4 +48,20 @@ public interface PlayerFocusEnforcer {
* Unmute players at the end of a call
*/
void unmutePlayersForCall();
+
+ /**
+ * Fade out whatever is still playing after the non-transient focus change
+ * @param winner the new non-transient focus owner
+ * @param loser the previous focus owner
+ * @return true if there were any active players for the loser that qualified for being
+ * faded out (because of audio attributes, or player types), and as such were faded
+ * out.
+ */
+ boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser);
+
+ /**
+ * Mark this UID as no longer playing a role in focus enforcement
+ * @param uid
+ */
+ void forgetUid(int uid);
} \ No newline at end of file
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/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index d7e08e462777..7a846f5b14a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -32,7 +32,6 @@ import com.android.server.biometrics.sensors.GenerateChallengeClient;
*/
public class FaceGenerateChallengeClient extends GenerateChallengeClient<ISession> {
private static final String TAG = "FaceGenerateChallengeClient";
- private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@@ -43,7 +42,7 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<ISessio
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
+ getFreshDaemon().generateChallenge(mSequentialId);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to generateChallenge", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index afa5bd22e081..b4c9b290cd06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -45,7 +45,7 @@ public class TestHal extends IFace.Stub {
return new ISession.Stub() {
@Override
- public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+ public void generateChallenge(int cookie) throws RemoteException {
Slog.w(TAG, "generateChallenge, cookie: " + cookie);
cb.onChallengeGenerated(0L);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 402886b0e92c..3c9cceddb5fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -32,7 +32,6 @@ import com.android.server.biometrics.sensors.GenerateChallengeClient;
*/
class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> {
private static final String TAG = "FingerprintGenerateChallengeClient";
- private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon,
@@ -45,7 +44,7 @@ class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISessio
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
+ getFreshDaemon().generateChallenge(mSequentialId);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to generateChallenge", e);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
index 9db2fcfd1b5b..8547a689dfb1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -45,7 +45,7 @@ public class TestHal extends IFingerprint.Stub {
return new ISession.Stub() {
@Override
- public void generateChallenge(int cookie, int timeoutSec) throws RemoteException {
+ public void generateChallenge(int cookie) throws RemoteException {
Slog.w(TAG, "generateChallenge, cookie: " + cookie);
cb.onChallengeGenerated(0L);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 2de709ebe71d..088249e81171 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -52,6 +52,7 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -59,6 +60,7 @@ import android.util.SparseArray;
import android.view.autofill.AutofillManagerInternal;
import android.widget.Toast;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -163,6 +165,10 @@ public class ClipboardService extends SystemService {
private static final boolean IS_EMULATOR =
SystemProperties.getBoolean("ro.kernel.qemu", false);
+ // DeviceConfig properties
+ private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
+ private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+
private final ActivityManagerInternal mAmInternal;
private final IUriGrantsManager mUgm;
private final UriGrantsManagerInternal mUgmInternal;
@@ -176,8 +182,14 @@ public class ClipboardService extends SystemService {
private HostClipboardMonitor mHostClipboardMonitor = null;
private Thread mHostMonitorThread = null;
+ @GuardedBy("mLock")
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
+ @GuardedBy("mLock")
+ private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+
+ private final Object mLock = new Object();
+
/**
* Instantiates the clipboard.
*/
@@ -204,7 +216,7 @@ public class ClipboardService extends SystemService {
new ClipData("host clipboard",
new String[]{"text/plain"},
new ClipData.Item(contents));
- synchronized(mClipboards) {
+ synchronized (mLock) {
setPrimaryClipInternal(getClipboard(0), clip,
android.os.Process.SYSTEM_UID, null);
}
@@ -213,6 +225,10 @@ public class ClipboardService extends SystemService {
mHostMonitorThread = new Thread(mHostClipboardMonitor);
mHostMonitorThread.start();
}
+
+ updateConfig();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,
+ getContext().getMainExecutor(), properties -> updateConfig());
}
@Override
@@ -222,11 +238,18 @@ public class ClipboardService extends SystemService {
@Override
public void onUserStopped(@NonNull TargetUser user) {
- synchronized (mClipboards) {
+ synchronized (mLock) {
mClipboards.remove(user.getUserIdentifier());
}
}
+ private void updateConfig() {
+ synchronized (mLock) {
+ mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ }
+ }
+
private class ListenerInfo {
final int mUid;
final String mPackageName;
@@ -472,7 +495,7 @@ public class ClipboardService extends SystemService {
};
private PerUserClipboard getClipboard(@UserIdInt int userId) {
- synchronized (mClipboards) {
+ synchronized (mLock) {
PerUserClipboard puc = mClipboards.get(userId);
if (puc == null) {
puc = new PerUserClipboard(userId);
@@ -849,9 +872,10 @@ public class ClipboardService extends SystemService {
if (clipboard.primaryClip == null) {
return;
}
- if (Settings.Global.getInt(getContext().getContentResolver(),
- "clipboard_access_toast_enabled", 1) == 0) {
- return;
+ synchronized (mLock) {
+ if (!mShowAccessNotifications) {
+ return;
+ }
}
// Don't notify if the app accessing the clipboard is the same as the current owner.
if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
@@ -880,8 +904,8 @@ public class ClipboardService extends SystemService {
CharSequence sourceAppLabel = null;
if (clipboard.mPrimaryClipPackage != null) {
try {
- sourceAppLabel = mPm.getApplicationLabel(
- mPm.getApplicationInfo(clipboard.mPrimaryClipPackage, 0));
+ sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
+ clipboard.mPrimaryClipPackage, 0, userId));
} catch (PackageManager.NameNotFoundException e) {
// leave label as null
}
@@ -889,7 +913,7 @@ public class ClipboardService extends SystemService {
try {
CharSequence callingAppLabel = mPm.getApplicationLabel(
- mPm.getApplicationInfo(callingPackage, 0));
+ mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
String message;
if (sourceAppLabel != null) {
message = callingAppLabel + " pasted from " + sourceAppLabel;
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index df83df9a73fb..5cf478a3ef1f 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -80,6 +80,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
/**
+ * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+ */
+ public CompatChange(Change change) {
+ this(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
+ change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
+ change.getDescription(), change.getOverridable());
+ }
+
+ /**
* @param changeId Unique ID for the change. See {@link android.compat.Compatibility}.
* @param name Short descriptive name.
* @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter};
@@ -93,15 +102,10 @@ public final class CompatChange extends CompatibilityChangeInfo {
boolean overridable) {
super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
description, overridable);
- }
- /**
- * @param change an object generated by services/core/xsd/platform-compat-config.xsd
- */
- public CompatChange(Change change) {
- super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
- change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
- change.getDescription(), change.getOverridable());
+ // Initialize override maps.
+ mEvaluatedOverrides = new HashMap<>();
+ mRawOverrides = new HashMap<>();
}
void registerListener(ChangeListener listener) {
@@ -127,18 +131,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString());
}
- if (mEvaluatedOverrides == null) {
- mEvaluatedOverrides = new HashMap<>();
- }
mEvaluatedOverrides.put(pname, enabled);
notifyListener(pname);
}
private void removePackageOverrideInternal(String pname) {
- if (mEvaluatedOverrides != null) {
- if (mEvaluatedOverrides.remove(pname) != null) {
- notifyListener(pname);
- }
+ if (mEvaluatedOverrides.remove(pname) != null) {
+ notifyListener(pname);
}
}
@@ -157,9 +156,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString());
}
- if (mRawOverrides == null) {
- mRawOverrides = new HashMap<>();
- }
mRawOverrides.put(packageName, override);
recheckOverride(packageName, allowedState, context);
}
@@ -212,7 +208,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
boolean hasPackageOverride(String pname) {
- return mRawOverrides != null && mRawOverrides.containsKey(pname);
+ return mRawOverrides.containsKey(pname);
}
/**
* Remove any package override for the given package name, restoring the default behaviour.
@@ -223,7 +219,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
*/
boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
Context context) {
- if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) {
+ if (mRawOverrides.remove(pname) != null) {
recheckOverride(pname, allowedState, context);
return true;
}
@@ -241,7 +237,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (app == null) {
return defaultValue();
}
- if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) {
+ if (mEvaluatedOverrides.containsKey(app.packageName)) {
return mEvaluatedOverrides.get(app.packageName);
}
if (getDisabled()) {
@@ -289,7 +285,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return true if there is such override
*/
private boolean hasOverride(String packageName) {
- return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName);
+ return mEvaluatedOverrides.containsKey(packageName);
}
/**
@@ -298,20 +294,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return true if there is such a deferred override
*/
private boolean hasRawOverride(String packageName) {
- return mRawOverrides != null && mRawOverrides.containsKey(packageName);
+ return mRawOverrides.containsKey(packageName);
}
- void loadOverrides(ChangeOverrides changeOverrides) {
- if (mRawOverrides == null) {
- mRawOverrides = new HashMap<>();
- }
+ void clearOverrides() {
mRawOverrides.clear();
-
- if (mEvaluatedOverrides == null) {
- mEvaluatedOverrides = new HashMap<>();
- }
mEvaluatedOverrides.clear();
+ }
+ void loadOverrides(ChangeOverrides changeOverrides) {
// Load deferred overrides for backwards compatibility
if (changeOverrides.getDeferred() != null) {
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
@@ -345,34 +336,30 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
ChangeOverrides saveOverrides() {
- if (mRawOverrides == null || mRawOverrides.isEmpty()) {
+ if (mRawOverrides.isEmpty()) {
return null;
}
ChangeOverrides changeOverrides = new ChangeOverrides();
changeOverrides.setChangeId(getId());
ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw();
List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue();
- if (mRawOverrides != null) {
- for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
- RawOverrideValue override = new RawOverrideValue();
- override.setPackageName(entry.getKey());
- override.setMinVersionCode(entry.getValue().getMinVersionCode());
- override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
- override.setEnabled(entry.getValue().getEnabled());
- rawList.add(override);
- }
+ for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
+ RawOverrideValue override = new RawOverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setMinVersionCode(entry.getValue().getMinVersionCode());
+ override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
+ override.setEnabled(entry.getValue().getEnabled());
+ rawList.add(override);
}
changeOverrides.setRaw(rawOverrides);
ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
- if (mEvaluatedOverrides != null) {
- for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
- OverrideValue override = new OverrideValue();
- override.setPackageName(entry.getKey());
- override.setEnabled(entry.getValue());
- validatedList.add(override);
- }
+ for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ validatedList.add(override);
}
changeOverrides.setValidated(validatedOverrides);
return changeOverrides;
@@ -394,10 +381,10 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (getLoggingOnly()) {
sb.append("; loggingOnly");
}
- if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) {
+ if (!mEvaluatedOverrides.isEmpty()) {
sb.append("; packageOverrides=").append(mEvaluatedOverrides);
}
- if (mRawOverrides != null && mRawOverrides.size() > 0) {
+ if (!mRawOverrides.isEmpty()) {
sb.append("; rawOverrides=").append(mRawOverrides);
}
if (getOverridable()) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 66a652053857..2c053b421904 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -67,6 +67,7 @@ final class CompatConfig {
private static final String TAG = "CompatConfig";
private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+ private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
@GuardedBy("mChanges")
@@ -94,8 +95,7 @@ final class CompatConfig {
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
- File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
- config.initOverrides(overridesFile);
+ config.initOverrides();
config.invalidateCache();
return config;
}
@@ -525,10 +525,34 @@ final class CompatConfig {
}
}
- void initOverrides(File overridesFile) {
+ private void initOverrides() {
+ initOverrides(new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE),
+ new File(STATIC_OVERRIDES_PRODUCT_DIR, OVERRIDES_FILE));
+ }
+
+ @VisibleForTesting
+ void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
+ // Clear overrides from all changes before loading.
+ synchronized (mChanges) {
+ for (int i = 0; i < mChanges.size(); ++i) {
+ mChanges.valueAt(i).clearOverrides();
+ }
+ }
+
+ loadOverrides(staticOverridesFile);
+
+ mOverridesFile = dynamicOverridesFile;
+ loadOverrides(dynamicOverridesFile);
+
+ if (staticOverridesFile.exists()) {
+ // Only save overrides if there is a static overrides file.
+ saveOverrides();
+ }
+ }
+
+ private void loadOverrides(File overridesFile) {
if (!overridesFile.exists()) {
- mOverridesFile = overridesFile;
- // There have not been any overrides added yet.
+ // Overrides file doesn't exist.
return;
}
@@ -548,7 +572,6 @@ final class CompatConfig {
Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
return;
}
- mOverridesFile = overridesFile;
}
/**
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 43d9ade67a11..4f6b5301e56f 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -19,6 +19,8 @@ package com.android.server.connectivity;
import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES;
import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
@@ -147,17 +149,18 @@ public class DnsManager {
}
public static class PrivateDnsValidationUpdate {
- final public int netId;
- final public InetAddress ipAddress;
- final public String hostname;
- final public boolean validated;
+ public final int netId;
+ public final InetAddress ipAddress;
+ public final String hostname;
+ // Refer to IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_*.
+ public final int validationResult;
public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
- String hostname, boolean validated) {
+ String hostname, int validationResult) {
this.netId = netId;
this.ipAddress = ipAddress;
this.hostname = hostname;
- this.validated = validated;
+ this.validationResult = validationResult;
}
}
@@ -216,10 +219,13 @@ public class DnsManager {
if (!mValidationMap.containsKey(p)) {
return;
}
- if (update.validated) {
+ if (update.validationResult == VALIDATION_RESULT_SUCCESS) {
mValidationMap.put(p, ValidationStatus.SUCCEEDED);
- } else {
+ } else if (update.validationResult == VALIDATION_RESULT_FAILURE) {
mValidationMap.put(p, ValidationStatus.FAILED);
+ } else {
+ Log.e(TAG, "Unknown private dns validation operation="
+ + update.validationResult);
}
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 34d9ccc15dba..7b20ded19205 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -57,8 +57,8 @@ import android.util.Log;
import android.util.Pair;
import com.android.internal.R;
-import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.HexDump;
import com.android.net.module.util.IpUtils;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 641287f0f435..fa80b25f9026 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -29,14 +29,12 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.RouteInfo;
-import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetworkStackConstants;
-import com.android.server.net.BaseNetworkObserver;
import java.net.Inet6Address;
import java.util.Objects;
@@ -48,7 +46,7 @@ import java.util.Objects;
*
* @hide
*/
-public class Nat464Xlat extends BaseNetworkObserver {
+public class Nat464Xlat {
private static final String TAG = Nat464Xlat.class.getSimpleName();
// This must match the interface prefix in clatd.c.
@@ -70,7 +68,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
private final IDnsResolver mDnsResolver;
private final INetd mNetd;
- private final INetworkManagementService mNMService;
// The network we're running on, and its type.
private final NetworkAgentInfo mNetwork;
@@ -99,11 +96,9 @@ public class Nat464Xlat extends BaseNetworkObserver {
private boolean mPrefixDiscoveryRunning;
- public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
- INetworkManagementService nmService) {
+ public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver) {
mDnsResolver = dnsResolver;
mNetd = netd;
- mNMService = nmService;
mNetwork = nai;
}
@@ -174,13 +169,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
* and set internal state.
*/
private void enterStartingState(String baseIface) {
- try {
- mNMService.registerObserver(this);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
- return;
- }
-
mNat64PrefixInUse = selectNat64Prefix();
String addrStr = null;
try {
@@ -216,11 +204,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
* Unregister as a base observer for the stacked interface, and clear internal state.
*/
private void leaveStartedState() {
- try {
- mNMService.unregisterObserver(this);
- } catch (RemoteException | IllegalStateException e) {
- Log.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
- }
mNat64PrefixInUse = null;
mIface = null;
mBaseIface = null;
@@ -507,12 +490,10 @@ public class Nat464Xlat extends BaseNetworkObserver {
stop();
}
- @Override
public void interfaceLinkStateChanged(String iface, boolean up) {
mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); });
}
- @Override
public void interfaceRemoved(String iface) {
mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4cf527415d7e..1d0e11569c80 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -35,7 +35,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.QosCallbackException;
import android.net.QosFilter;
import android.net.QosFilterParcelable;
@@ -43,7 +43,6 @@ import android.net.QosSession;
import android.net.TcpKeepalivePacketData;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
import android.telephony.data.EpsBearerQosSessionAttributes;
@@ -341,8 +340,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
@NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
- IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
- int creatorUid, QosCallbackTracker qosCallbackTracker) {
+ IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
+ QosCallbackTracker qosCallbackTracker) {
Objects.requireNonNull(net);
Objects.requireNonNull(info);
Objects.requireNonNull(lp);
@@ -356,7 +355,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp;
networkCapabilities = nc;
mScore = score;
- clatd = new Nat464Xlat(this, netd, dnsResolver, nms);
+ clatd = new Nat464Xlat(this, netd, dnsResolver);
mConnService = connService;
mContext = context;
mHandler = handler;
@@ -891,15 +890,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
mScore = score;
}
- public NetworkState getNetworkState() {
+ /**
+ * Return a {@link NetworkStateSnapshot} for this network.
+ */
+ @NonNull
+ public NetworkStateSnapshot getNetworkStateSnapshot() {
synchronized (this) {
// Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
final String subscriberId = (networkAgentConfig != null)
? networkAgentConfig.subscriberId : null;
- return new NetworkState(new NetworkInfo(networkInfo),
- new LinkProperties(linkProperties),
- new NetworkCapabilities(networkCapabilities), network, subscriberId);
+ return new NetworkStateSnapshot(network, new NetworkCapabilities(networkCapabilities),
+ new LinkProperties(linkProperties), subscriberId, networkInfo.getType());
}
}
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/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 835b4688dd4e..658d27f43313 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -425,7 +425,13 @@ public final class DeviceStateManagerService extends SystemService {
if (!mRequestRecords.isEmpty()) {
final OverrideRequestRecord topRequest =
mRequestRecords.get(mRequestRecords.size() - 1);
- topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+ if (topRequest.mRequestedState.getIdentifier() == mCommittedState.getIdentifier()) {
+ // The top request could have come in while the service was awaiting callback
+ // from the policy. In that case we only set it to active if it matches the
+ // current committed state, otherwise it will be set to active when its
+ // requested state is committed.
+ topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+ }
}
mPendingState = Optional.empty();
@@ -563,10 +569,13 @@ public final class DeviceStateManagerService extends SystemService {
new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
mRequestRecords.add(request);
processRecord.mRequestRecords.put(request.mToken, request);
- // We don't set the status of the new request to ACTIVE here as it will be set in
- // commitPendingState().
- updatePendingStateLocked();
+ final boolean updatedPendingState = updatePendingStateLocked();
+ if (!updatedPendingState && !mPendingState.isPresent()) {
+ // We don't set the status of the new request to ACTIVE if the request updated the
+ // pending state as it will be set in commitPendingState().
+ request.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE, true /* markDirty */);
+ }
}
notifyRequestsOfStatusChangeIfNeeded();
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index d4556ed5f9fa..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,15 +48,14 @@ class DeviceStateToLayoutMap {
public static final int STATE_DEFAULT = DeviceStateManager.INVALID_DEVICE_STATE;
- // TODO - b/168208162 - Remove these when we check in static definitions for layouts
- public static final int STATE_FOLDED = 100;
- public static final int STATE_UNFOLDED = 101;
+ 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) {
@@ -68,7 +76,7 @@ class DeviceStateToLayoutMap {
return layout;
}
- private Layout create(int state) {
+ private Layout createLayout(int state) {
if (mLayoutMap.contains(state)) {
Slog.e(TAG, "Attempted to create a second layout for state " + state);
return null;
@@ -79,43 +87,37 @@ class DeviceStateToLayoutMap {
return layout;
}
- 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;
- }
+ /**
+ * Reads display-layout-configuration files to get the layouts to use for this device.
+ */
+ 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);
- // Only add folded states if folded state config is not empty
- if (foldedDeviceStates.length == 0) {
- return;
+ 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);
}
-
- // Create the folded state layout
- final Layout foldedLayout = create(STATE_FOLDED);
- foldedLayout.createDisplayLocked(
- DisplayAddress.fromPhysicalDisplayId(displayIds[0]), true /*isDefault*/);
-
- // Create the unfolded state layout
- final Layout unfoldedLayout = create(STATE_UNFOLDED);
- unfoldedLayout.createDisplayLocked(
- DisplayAddress.fromPhysicalDisplayId(displayIds[1]), true /*isDefault*/);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index 663883ab2825..2dcd5ccaf557 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -30,6 +30,8 @@ public class DisplayGroup {
private final List<LogicalDisplay> mDisplays = new ArrayList<>();
private final int mGroupId;
+ private int mChangeCount;
+
DisplayGroup(int groupId) {
mGroupId = groupId;
}
@@ -45,11 +47,16 @@ public class DisplayGroup {
* @param display the {@link LogicalDisplay} to add to the Group
*/
void addDisplayLocked(LogicalDisplay display) {
- if (!mDisplays.contains(display)) {
+ if (!containsLocked(display)) {
+ mChangeCount++;
mDisplays.add(display);
}
}
+ boolean containsLocked(LogicalDisplay display) {
+ return mDisplays.contains(display);
+ }
+
/**
* Removes the provided {@code display} from the Group.
*
@@ -57,6 +64,7 @@ public class DisplayGroup {
* @return {@code true} if the {@code display} was removed; otherwise {@code false}
*/
boolean removeDisplayLocked(LogicalDisplay display) {
+ mChangeCount++;
return mDisplays.remove(display);
}
@@ -65,6 +73,11 @@ public class DisplayGroup {
return mDisplays.isEmpty();
}
+ /** Returns a count of the changes made to this display group. */
+ int getChangeCountLocked() {
+ return mChangeCount;
+ }
+
/** Returns the number of {@link LogicalDisplay LogicalDisplays} in the Group. */
int getSizeLocked() {
return mDisplays.size();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 52149ee3a4dd..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);
@@ -485,13 +485,13 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
long timeout = SystemClock.uptimeMillis()
+ mInjector.getDefaultDisplayDelayTimeout();
- while (mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY) == null
+ while (mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY) == null
|| mVirtualDisplayAdapter == null) {
long delay = timeout - SystemClock.uptimeMillis();
if (delay <= 0) {
throw new RuntimeException("Timeout waiting for default display "
+ "to be initialized. DefaultDisplay="
- + mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY)
+ + mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)
+ ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
}
if (DEBUG) {
@@ -549,7 +549,7 @@ public final class DisplayManagerService extends SystemService {
mSystemReady = true;
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
- recordTopInsetLocked(mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY));
+ recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
updateSettingsLocked();
}
@@ -617,7 +617,7 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
void setDisplayInfoOverrideFromWindowManagerInternal(int displayId, DisplayInfo info) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
@@ -632,7 +632,7 @@ public final class DisplayManagerService extends SystemService {
*/
private void getNonOverrideDisplayInfoInternal(int displayId, DisplayInfo outInfo) {
synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
display.getNonOverrideDisplayInfoLocked(outInfo);
}
@@ -691,8 +691,8 @@ public final class DisplayManagerService extends SystemService {
mDisplayStates.setValueAt(index, state);
mDisplayBrightnesses.setValueAt(index, brightnessState);
- runnable = updateDisplayStateLocked(
- mLogicalDisplayMapper.getLocked(displayId).getPrimaryDisplayDeviceLocked());
+ runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
+ .getPrimaryDisplayDeviceLocked());
}
// Setting the display power state can take hundreds of milliseconds
@@ -803,9 +803,9 @@ public final class DisplayManagerService extends SystemService {
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
- DisplayInfo info =
+ final DisplayInfo info =
getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
display.getDisplayInfoLocked(), callingUid);
if (info.hasAccess(callingUid)
@@ -952,7 +952,7 @@ public final class DisplayManagerService extends SystemService {
private void requestColorModeInternal(int displayId, int colorMode) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null &&
display.getRequestedColorModeLocked() != colorMode) {
display.setRequestedColorModeLocked(colorMode);
@@ -989,7 +989,7 @@ public final class DisplayManagerService extends SystemService {
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
return display.getDisplayIdLocked();
}
@@ -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,10 +1203,7 @@ 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.getLocked(device);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
final int state;
final int displayId = display.getDisplayIdLocked();
@@ -1364,7 +1364,7 @@ public final class DisplayManagerService extends SystemService {
float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
boolean inTraversal) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
@@ -1406,7 +1406,7 @@ public final class DisplayManagerService extends SystemService {
private void setDisplayOffsetsInternal(int displayId, int x, int y) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
@@ -1424,7 +1424,7 @@ public final class DisplayManagerService extends SystemService {
private void setDisplayScalingDisabledInternal(int displayId, boolean disable) {
synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display == null) {
return;
}
@@ -1460,7 +1460,7 @@ public final class DisplayManagerService extends SystemService {
@Nullable
private IBinder getDisplayToken(int displayId) {
synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
if (device != null) {
@@ -1478,7 +1478,7 @@ public final class DisplayManagerService extends SystemService {
if (token == null) {
return null;
}
- final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (logicalDisplay == null) {
return null;
}
@@ -1611,15 +1611,16 @@ public final class DisplayManagerService extends SystemService {
// Find the logical display that the display device is showing.
// Certain displays only ever show their own content.
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
+ LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (!ownContent) {
if (display != null && !display.hasContentLocked()) {
// If the display does not have any content of its own, then
// automatically mirror the requested logical display contents if possible.
- display = mLogicalDisplayMapper.getLocked(device.getDisplayIdToMirrorLocked());
+ display = mLogicalDisplayMapper.getDisplayLocked(
+ device.getDisplayIdToMirrorLocked());
}
if (display == null) {
- display = mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY);
+ display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
}
}
@@ -1896,9 +1897,9 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
DisplayDeviceInfo getDisplayDeviceInfoInternal(int displayId) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
- DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
+ final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayDeviceInfoLocked();
}
return null;
@@ -1908,9 +1909,9 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
int getDisplayIdToMirrorInternal(int displayId) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
- DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
+ final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
return displayDevice.getDisplayIdToMirrorLocked();
}
return Display.INVALID_DISPLAY;
@@ -1992,7 +1993,8 @@ public final class DisplayManagerService extends SystemService {
ArraySet<Integer> uids;
synchronized (mSyncRoot) {
int displayId = msg.arg1;
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display =
+ mLogicalDisplayMapper.getDisplayLocked(displayId);
uids = display.getPendingFrameRateOverrideUids();
display.clearPendingFrameRateOverrideUids();
}
@@ -2586,7 +2588,7 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public boolean isMinimalPostProcessingRequested(int displayId) {
synchronized (mSyncRoot) {
- return mLogicalDisplayMapper.getLocked(displayId)
+ return mLogicalDisplayMapper.getDisplayLocked(displayId)
.getRequestedMinimalPostProcessingLocked();
}
}
@@ -2831,7 +2833,7 @@ public final class DisplayManagerService extends SystemService {
@Override
public Point getDisplayPosition(int displayId) {
synchronized (mSyncRoot) {
- LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
return display.getDisplayPosition();
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index dce6bd849953..645ca7ac33e0 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -31,7 +31,6 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -80,6 +79,8 @@ public class DisplayModeDirector {
// specific display.
private static final int GLOBAL_ID = -1;
+ private static final int INVALID_DISPLAY_MODE_ID = -1;
+
// The tolerance within which we consider something approximately equals.
private static final float FLOAT_TOLERANCE = 0.01f;
@@ -322,12 +323,30 @@ public class DisplayModeDirector {
appRequestSummary.maxRefreshRate));
}
- // If the application requests a given mode with preferredModeId function, it will be
- // stored as baseModeId.
- int baseModeId = defaultMode.getModeId();
- if (availableModes.length > 0) {
+ int baseModeId = INVALID_DISPLAY_MODE_ID;
+
+ // Select the default mode if available. This is important because SurfaceFlinger
+ // can do only seamless switches by default. Some devices (e.g. TV) don't support
+ // seamless switching so the mode we select here won't be changed.
+ for (int availableMode : availableModes) {
+ if (availableMode == defaultMode.getModeId()) {
+ baseModeId = defaultMode.getModeId();
+ break;
+ }
+ }
+
+ // If the application requests a display mode by setting
+ // LayoutParams.preferredDisplayModeId, it will be the only available mode and it'll
+ // be stored as baseModeId.
+ if (baseModeId == INVALID_DISPLAY_MODE_ID && availableModes.length > 0) {
baseModeId = availableModes[0];
}
+
+ if (baseModeId == INVALID_DISPLAY_MODE_ID) {
+ throw new IllegalStateException("Can't select a base display mode for display "
+ + displayId + ". The votes are " + mVotesByDisplay.valueAt(displayId));
+ }
+
if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
Display.Mode baseMode = null;
for (Display.Mode mode : modes) {
@@ -351,6 +370,7 @@ public class DisplayModeDirector {
boolean allowGroupSwitching =
mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+
return new DesiredDisplayModeSpecs(baseModeId,
allowGroupSwitching,
new RefreshRateRange(
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3709963b7caa..a0d93656b7f6 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -767,6 +767,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
private void setDisplayBrightness(float brightness) {
+ // Ensure brightnessState is valid, before processing and sending to
+ // surface control
+ if (Float.isNaN(brightness)) {
+ return;
+ }
+
if (DEBUG) {
Slog.d(TAG, "setDisplayBrightness("
+ "id=" + physicalDisplayId
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 20b133ce4d0a..d9570c710b15 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -17,11 +17,11 @@
package com.android.server.display;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
import android.util.ArraySet;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayEventReceiver;
@@ -64,12 +64,14 @@ import java.util.Objects;
*/
final class LogicalDisplay {
private static final String TAG = "LogicalDisplay";
- private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
// of its content from appearing.
private static final int BLANK_LAYER_STACK = -1;
+ private static final DisplayInfo EMPTY_DISPLAY_INFO = new DisplayInfo();
+
+ private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
private final int mDisplayId;
private final int mLayerStack;
@@ -297,7 +299,7 @@ final class LogicalDisplay {
// Check whether logical display has become invalid.
if (!deviceRepo.containsLocked(mPrimaryDisplayDevice)) {
- mPrimaryDisplayDevice = null;
+ setPrimaryDisplayDeviceLocked(null);
return;
}
@@ -684,18 +686,28 @@ final class LogicalDisplay {
* @param targetDisplay The display with which to swap display-devices.
* @return {@code true} if the displays were swapped, {@code false} otherwise.
*/
- public boolean swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
- final DisplayDevice targetDevice = targetDisplay.getPrimaryDisplayDeviceLocked();
- if (mPrimaryDisplayDevice == null || targetDevice == null) {
- Slog.e(TAG, "Missing display device during swap: " + mPrimaryDisplayDevice + " , "
- + targetDevice);
- return false;
- }
+ public void swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
+ final DisplayDevice oldTargetDevice =
+ targetDisplay.setPrimaryDisplayDeviceLocked(mPrimaryDisplayDevice);
+ setPrimaryDisplayDeviceLocked(oldTargetDevice);
+ }
+
+ /**
+ * Sets the primary display device to the specified device.
+ *
+ * @param device The new device to set.
+ * @return The previously set display device.
+ */
+ public DisplayDevice setPrimaryDisplayDeviceLocked(@Nullable DisplayDevice device) {
+ final DisplayDevice old = mPrimaryDisplayDevice;
+ mPrimaryDisplayDevice = device;
- final DisplayDevice tmpDevice = mPrimaryDisplayDevice;
- mPrimaryDisplayDevice = targetDisplay.mPrimaryDisplayDevice;
- targetDisplay.mPrimaryDisplayDevice = tmpDevice;
- return true;
+ // Reset all our display info data
+ mPrimaryDisplayDeviceInfo = null;
+ mBaseDisplayInfo.copyFrom(EMPTY_DISPLAY_INFO);
+ mInfo.set(null);
+
+ return old;
}
/**
@@ -718,8 +730,8 @@ final class LogicalDisplay {
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
- pw.println("mLayerStack=" + mLayerStack);
pw.println("mIsEnabled=" + mIsEnabled);
+ pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
pw.println("mRequestedColorMode=" + mRequestedColorMode);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index a3ff534e336e..d6826be248df 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -16,14 +16,16 @@
package com.android.server.display;
-import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.Display;
-import android.view.DisplayEventReceiver;
+import android.view.DisplayAddress;
import android.view.DisplayInfo;
import com.android.server.display.layout.Layout;
@@ -74,49 +76,79 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private final boolean mSingleDisplayDemoMode;
/**
- * List of all logical displays indexed by logical display id.
+ * Map of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
*/
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
- /** A mapping from logical display id to display group. */
- private final SparseArray<DisplayGroup> mDisplayIdToGroupMap = new SparseArray<>();
+ /** Map of all display groups indexed by display group id. */
+ private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
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.
+ * Any entry in here requires us to send a {@link LOGICAL_DISPLAY_EVENT_REMOVED} event when it
+ * is deleted or {@link LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed.
+ */
+ private final SparseBooleanArray mUpdatedLogicalDisplays = new SparseBooleanArray();
+
+ /**
+ * Keeps track of all the display groups that we already told other people about. IOW, if a
+ * display group is in this array, then we *must* send change and remove notifications for it
+ * because other components know about them. Also, what this array stores is a change counter
+ * for each group, so we know if the group itself has changes since we last sent out a
+ * notification. See {@link DisplayGroup#getChangeCountLocked}.
+ */
+ private final SparseIntArray mUpdatedDisplayGroups = new SparseIntArray();
+
+ /**
+ * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
+ */
+ private final SparseIntArray mLogicalDisplaysToUpdate = new SparseIntArray();
+
+ /**
+ * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
+ */
+ private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
private Layout mCurrentLayout = null;
- private boolean mIsFolded = false;
+ 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
public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
switch (event) {
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
+ if (DEBUG) {
+ Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+ }
handleDisplayDeviceAddedLocked(device);
break;
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
+ if (DEBUG) {
+ Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
+ }
updateLogicalDisplaysLocked();
break;
case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
+ if (DEBUG) {
+ Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+ }
updateLogicalDisplaysLocked();
break;
}
@@ -127,11 +159,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mListener.onTraversalRequested();
}
- public LogicalDisplay getLocked(int displayId) {
+ public LogicalDisplay getDisplayLocked(int displayId) {
return mLogicalDisplays.get(displayId);
}
- public LogicalDisplay getLocked(DisplayDevice device) {
+ public LogicalDisplay getDisplayLocked(DisplayDevice device) {
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
@@ -166,16 +198,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
}
- public DisplayGroup getDisplayGroupLocked(int groupId) {
- final int size = mDisplayIdToGroupMap.size();
+ public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
+ final LogicalDisplay display = getDisplayLocked(displayId);
+ if (display == null) {
+ return Display.INVALID_DISPLAY_GROUP;
+ }
+
+ final int size = mDisplayGroups.size();
for (int i = 0; i < size; i++) {
- final DisplayGroup displayGroup = mDisplayIdToGroupMap.valueAt(i);
- if (displayGroup.getGroupId() == groupId) {
- return displayGroup;
+ final DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+ if (displayGroup.containsLocked(display)) {
+ return mDisplayGroups.keyAt(i);
}
}
- return null;
+ return Display.INVALID_DISPLAY_GROUP;
+ }
+
+ public DisplayGroup getDisplayGroupLocked(int groupId) {
+ return mDisplayGroups.get(groupId);
}
public void dumpLocked(PrintWriter pw) {
@@ -203,229 +244,334 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
void setDeviceStateLocked(int state) {
- boolean folded = false;
- for (int i = 0; i < mFoldedDeviceStates.length; i++) {
- if (state == mFoldedDeviceStates[i]) {
- folded = true;
- break;
- }
+ if (state != mDeviceState) {
+ resetLayoutLocked();
+ mDeviceState = state;
+ applyLayoutLocked();
+ updateLogicalDisplaysLocked();
}
- setDeviceFoldedLocked(folded);
}
- void setDeviceFoldedLocked(boolean isFolded) {
- mIsFolded = isFolded;
+ private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+ DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
+ // Internal Displays need to have additional initialization.
+ // TODO: b/168208162 - This initializes a default dynamic display layout for INTERNAL
+ // devices, which will eventually just be a fallback in case no static layout definitions
+ // exist or cannot be loaded.
+ if (deviceInfo.type == Display.TYPE_INTERNAL) {
+ initializeInternalDisplayDeviceLocked(device);
+ }
- // Until we have fully functioning state mapping, use hardcoded states based on isFolded
- final int state = mIsFolded ? DeviceStateToLayoutMap.STATE_FOLDED
- : DeviceStateToLayoutMap.STATE_UNFOLDED;
+ // Create a logical display for the new display device
+ LogicalDisplay display = createNewLogicalDisplayLocked(
+ device, Layout.assignDisplayIdLocked(false /*isDefault*/));
- if (DEBUG) {
- Slog.d(TAG, "New device state: " + state);
- }
+ applyLayoutLocked();
+ updateLogicalDisplaysLocked();
+ }
- final Layout layout = mDeviceStateToLayoutMap.get(state);
- if (layout == null) {
- return;
- }
- final Layout.Display displayLayout = layout.getById(Display.DEFAULT_DISPLAY);
- if (displayLayout == null) {
- return;
- }
- final DisplayDevice newDefaultDevice =
- mDisplayDeviceRepo.getByAddressLocked(displayLayout.getAddress());
- if (newDefaultDevice == null) {
- return;
- }
+ /**
+ * Updates the rest of the display system once all the changes are applied for display
+ * devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
+ * creating/adjusting/removing DisplayGroups, and notifying the rest of the system of the
+ * relevant changes.
+ */
+ private void updateLogicalDisplaysLocked() {
+ // Go through all the displays and figure out if they need to be updated.
+ // Loops in reverse so that displays can be removed during the loop without affecting the
+ // rest of the loop.
+ for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
+ final int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
- final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
- mCurrentLayout = layout;
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
- // If we're already set up accurately, return early
- if (defaultDisplay.getPrimaryDisplayDeviceLocked() == newDefaultDevice) {
- return;
- }
+ display.updateLocked(mDisplayDeviceRepo);
+ final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
+ final boolean wasPreviouslyUpdated = mUpdatedLogicalDisplays.get(displayId);
- // We need to swap the default display's display-device with the one that is supposed
- // to be the default in the new layout.
- final LogicalDisplay displayToSwap = getLocked(newDefaultDevice);
- if (displayToSwap == null) {
- Slog.w(TAG, "Canceling display swap - unexpected empty second display for: "
- + newDefaultDevice);
- return;
- }
- defaultDisplay.swapDisplaysLocked(displayToSwap);
+ // The display is no longer valid and needs to be removed.
+ if (!display.isValidLocked()) {
+ mUpdatedLogicalDisplays.delete(displayId);
- // We ensure that the non-default Display is always forced to be off. This was likely
- // already done in a previous iteration, but we do it with each swap in case something in
- // the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
- defaultDisplay.setEnabled(true);
- displayToSwap.setEnabled(false);
+ // Remove from group
+ final DisplayGroup displayGroup = getDisplayGroupLocked(
+ getDisplayGroupIdFromDisplayIdLocked(displayId));
+ if (displayGroup != null) {
+ displayGroup.removeDisplayLocked(display);
+ }
- // Update the world
- updateLogicalDisplaysLocked();
- }
+ if (wasPreviouslyUpdated) {
+ // The display isn't actually removed from our internal data structures until
+ // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
+ Slog.i(TAG, "Removing display: " + displayId);
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ } else {
+ // This display never left this class, safe to remove without notification
+ mLogicalDisplays.removeAt(i);
+ }
+ continue;
+
+ // The display is new.
+ } else if (!wasPreviouslyUpdated) {
+ Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
+ assignDisplayGroupLocked(display);
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
+
+ // Underlying displays device has changed to a different one.
+ } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
+ // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
+ assignDisplayGroupLocked(display);
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
+
+ // Something about the display device has changed.
+ } else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
+ // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
+ assignDisplayGroupLocked(display);
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
+
+ // Display frame rate overrides changed.
+ } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
+ mLogicalDisplaysToUpdate.put(
+ displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
- private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
- DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
- boolean isDefault = (deviceInfo.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
- if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
- Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
- isDefault = false;
+ // Non-override display values changed.
+ } else {
+ // While application shouldn't know nor care about the non-overridden info, we
+ // still need to let WindowManager know so it can update its own internal state for
+ // things like display cutouts.
+ display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
+ if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
+ }
+ }
+
+ mUpdatedLogicalDisplays.put(displayId, true);
}
- if (!isDefault && mSingleDisplayDemoMode) {
- Slog.i(TAG, "Not creating a logical display for a secondary display "
- + " because single display demo mode is enabled: " + deviceInfo);
- return;
+ // Go through the groups and do the same thing. We do this after displays since group
+ // information can change in the previous loop.
+ // Loops in reverse so that groups can be removed during the loop without affecting the
+ // rest of the loop.
+ for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
+ final int groupId = mDisplayGroups.keyAt(i);
+ final DisplayGroup group = mDisplayGroups.valueAt(i);
+ final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) < 0;
+ final int changeCount = group.getChangeCountLocked();
+
+ if (group.isEmptyLocked()) {
+ mUpdatedDisplayGroups.delete(groupId);
+ if (wasPreviouslyUpdated) {
+ mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
+ }
+ continue;
+ } else if (!wasPreviouslyUpdated) {
+ mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
+ } else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
+ mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
+ }
+ mUpdatedDisplayGroups.put(groupId, changeCount);
}
- final int displayId = Layout.assignDisplayIdLocked(isDefault);
- final int layerStack = assignLayerStackLocked(displayId);
+ // Send the display and display group updates in order by message type. This is important
+ // to ensure that addition and removal notifications happen in the right order.
+ sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
+ sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
+ sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);
+
+ mLogicalDisplaysToUpdate.clear();
+ mDisplayGroupsToUpdate.clear();
+ }
- final DisplayGroup displayGroup;
- final boolean addNewDisplayGroup =
- isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0;
- if (addNewDisplayGroup) {
- final int groupId = assignDisplayGroupIdLocked(isDefault);
- displayGroup = new DisplayGroup(groupId);
- } else {
- displayGroup = mDisplayIdToGroupMap.get(Display.DEFAULT_DISPLAY);
- }
+ /**
+ * Send the specified message for all relevant displays in the specified display-to-message map.
+ */
+ private void sendUpdatesForDisplaysLocked(int msg) {
+ for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
+ final int currMsg = mLogicalDisplaysToUpdate.valueAt(i);
+ if (currMsg != msg) {
+ continue;
+ }
- LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
- display.updateDisplayGroupIdLocked(displayGroup.getGroupId());
- display.updateLocked(mDisplayDeviceRepo);
- if (!display.isValidLocked()) {
- // This should never happen currently.
- Slog.w(TAG, "Ignoring display device because the logical display "
- + "created from it was not considered valid: " + deviceInfo);
- return;
+ final int id = mLogicalDisplaysToUpdate.keyAt(i);
+ mListener.onLogicalDisplayEventLocked(getDisplayLocked(id), msg);
+ if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+ // We wait until we sent the EVENT_REMOVED event before actually removing the
+ // display.
+ mLogicalDisplays.delete(id);
+ }
}
+ }
+
+ /**
+ * Send the specified message for all relevant display groups in the specified message map.
+ */
+ private void sendUpdatesForGroupsLocked(int msg) {
+ for (int i = mDisplayGroupsToUpdate.size() - 1; i >= 0; --i) {
+ final int currMsg = mDisplayGroupsToUpdate.valueAt(i);
+ if (currMsg != msg) {
+ continue;
+ }
- // For foldable devices, we start the internal non-default displays as disabled.
- // TODO - b/168208162 - this will be removed when we recalculate the layout with each
- // display-device addition.
- if (mFoldedDeviceStates.length > 0 && deviceInfo.type == Display.TYPE_INTERNAL
- && !isDefault) {
- display.setEnabled(false);
+ final int id = mDisplayGroupsToUpdate.keyAt(i);
+ mListener.onDisplayGroupEventLocked(id, msg);
+ if (msg == DISPLAY_GROUP_EVENT_REMOVED) {
+ // We wait until we sent the EVENT_REMOVED event before actually removing the
+ // group.
+ mDisplayGroups.delete(id);
+ }
}
+ }
- mLogicalDisplays.put(displayId, display);
- displayGroup.addDisplayLocked(display);
- mDisplayIdToGroupMap.append(displayId, displayGroup);
+ private void assignDisplayGroupLocked(LogicalDisplay display) {
+ final int displayId = display.getDisplayIdLocked();
- if (addNewDisplayGroup) {
- // Group added events happen before Logical Display added events.
- mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
- LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
- }
+ // Get current display group data
+ int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
+ final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
- mListener.onLogicalDisplayEventLocked(display,
- LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED);
+ // Get the new display group if a change is needed
+ final DisplayInfo info = display.getDisplayInfoLocked();
+ final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
+ final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
+ if (groupId == Display.INVALID_DISPLAY_GROUP
+ || hasOwnDisplayGroup != needsOwnDisplayGroup) {
+ groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
+ }
- if (!addNewDisplayGroup) {
- // Group changed events happen after Logical Display added events.
- mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
- LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
+ // Create a new group if needed
+ DisplayGroup newGroup = getDisplayGroupLocked(groupId);
+ if (newGroup == null) {
+ newGroup = new DisplayGroup(groupId);
+ mDisplayGroups.append(groupId, newGroup);
+ }
+ if (oldGroup != newGroup) {
+ if (oldGroup != null) {
+ oldGroup.removeDisplayLocked(display);
+ }
+ newGroup.addDisplayLocked(display);
+ display.updateDisplayGroupIdLocked(groupId);
+ Slog.i(TAG, "Setting new display group " + groupId + " for display "
+ + displayId + ", from previous group: "
+ + (oldGroup != null ? oldGroup.getGroupId() : "null"));
}
+ }
- if (DEBUG) {
- Slog.d(TAG, "New Display added: " + display);
+ /**
+ * 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);
+ for (int i = layout.size() - 1; i >= 0; i--) {
+ final Layout.Display displayLayout = layout.getAt(i);
+ final LogicalDisplay display = getDisplayLocked(displayLayout.getLogicalDisplayId());
+ if (display != null) {
+ enableDisplayLocked(display, true); // Reset all displays back to enabled
+ }
}
}
+
/**
- * Updates all existing logical displays given the current set of display devices.
- * Removes invalid logical displays. Sends notifications if needed.
+ * Apply (or reapply) the currently selected display layout.
*/
- private void updateLogicalDisplaysLocked() {
- for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
- final int displayId = mLogicalDisplays.keyAt(i);
- LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ private void applyLayoutLocked() {
+ final Layout layout = mDeviceStateToLayoutMap.get(mDeviceState);
+ mCurrentLayout = layout;
+ Slog.i(TAG, "Applying the display layout for device state(" + mDeviceState
+ + "): " + layout);
- mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
- display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
- DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides =
- display.getFrameRateOverrides();
- display.updateLocked(mDisplayDeviceRepo);
- final DisplayGroup changedDisplayGroup;
- if (!display.isValidLocked()) {
- mLogicalDisplays.removeAt(i);
- final DisplayGroup displayGroup = mDisplayIdToGroupMap.removeReturnOld(displayId);
- displayGroup.removeDisplayLocked(display);
-
- mListener.onLogicalDisplayEventLocked(display,
- LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
-
- changedDisplayGroup = displayGroup;
- } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
- final int flags = display.getDisplayInfoLocked().flags;
- final DisplayGroup defaultDisplayGroup = mDisplayIdToGroupMap.get(
- Display.DEFAULT_DISPLAY);
- if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) {
- // The display should have its own DisplayGroup.
- if (defaultDisplayGroup.removeDisplayLocked(display)) {
- final int groupId = assignDisplayGroupIdLocked(false);
- final DisplayGroup displayGroup = new DisplayGroup(groupId);
- displayGroup.addDisplayLocked(display);
- display.updateDisplayGroupIdLocked(groupId);
- mDisplayIdToGroupMap.append(display.getDisplayIdLocked(), displayGroup);
- mListener.onDisplayGroupEventLocked(displayGroup.getGroupId(),
- LogicalDisplayMapper.DISPLAY_GROUP_EVENT_ADDED);
- changedDisplayGroup = defaultDisplayGroup;
- } else {
- changedDisplayGroup = null;
- }
- } else {
- // The display should be a part of the default DisplayGroup.
- final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId);
- if (displayGroup != defaultDisplayGroup) {
- displayGroup.removeDisplayLocked(display);
- defaultDisplayGroup.addDisplayLocked(display);
- display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
- mListener.onDisplayGroupEventLocked(defaultDisplayGroup.getGroupId(),
- LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED);
- mDisplayIdToGroupMap.put(displayId, defaultDisplayGroup);
- changedDisplayGroup = displayGroup;
- } else {
- changedDisplayGroup = null;
- }
- }
+ // Go through each of the displays in the current layout set.
+ final int size = layout.size();
+ for (int i = 0; i < size; i++) {
+ final Layout.Display displayLayout = layout.getAt(i);
+
+ // 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. 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) {
+ Slog.w(TAG, "The display device (" + address + "), is not available"
+ + " for the display state " + mDeviceState);
+ continue;
+ }
- final String oldUniqueId = mTempDisplayInfo.uniqueId;
- final String newUniqueId = display.getDisplayInfoLocked().uniqueId;
- final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId)
- ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED;
- mListener.onLogicalDisplayEventLocked(display, eventMsg);
- } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
- mListener.onLogicalDisplayEventLocked(display,
- LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
- changedDisplayGroup = null;
- } else {
- // While applications shouldn't know nor care about the non-overridden info, we
- // still need to let WindowManager know so it can update its own internal state for
- // things like display cutouts.
- display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
- if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- mListener.onLogicalDisplayEventLocked(display,
- LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
- }
- changedDisplayGroup = null;
+ // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
+ // right one, if it doesn't exist, create a new one.
+ final int logicalDisplayId = displayLayout.getLogicalDisplayId();
+ LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
+ if (newDisplay == null) {
+ newDisplay = createNewLogicalDisplayLocked(
+ null /*displayDevice*/, logicalDisplayId);
}
- // CHANGED and REMOVED DisplayGroup events should always fire after Display events.
- if (changedDisplayGroup != null) {
- final int event = changedDisplayGroup.isEmptyLocked()
- ? LogicalDisplayMapper.DISPLAY_GROUP_EVENT_REMOVED
- : LogicalDisplayMapper.DISPLAY_GROUP_EVENT_CHANGED;
- mListener.onDisplayGroupEventLocked(changedDisplayGroup.getGroupId(), event);
+ // Now swap the underlying display devices between the old display and the new display
+ final LogicalDisplay oldDisplay = getDisplayLocked(device);
+ if (newDisplay != oldDisplay) {
+ newDisplay.swapDisplaysLocked(oldDisplay);
}
+ enableDisplayLocked(newDisplay, displayLayout.isEnabled());
}
}
- private int assignDisplayGroupIdLocked(boolean isDefault) {
- return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
+
+ /**
+ * Creates a new logical display for the specified device and display Id and adds it to the list
+ * of logical displays.
+ *
+ * @param device The device to associate with the LogicalDisplay.
+ * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
+ * @param isDefault Indicates if we are creating the default display.
+ * @return The new logical display if created, null otherwise.
+ */
+ private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
+ final int layerStack = assignLayerStackLocked(displayId);
+ final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+ display.updateLocked(mDisplayDeviceRepo);
+ mLogicalDisplays.put(displayId, display);
+ enableDisplayLocked(display, device != null);
+ return display;
+ }
+
+ private void enableDisplayLocked(LogicalDisplay display, boolean isEnabled) {
+ final int displayId = display.getDisplayIdLocked();
+ final DisplayInfo info = display.getDisplayInfoLocked();
+
+ final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
+ && (info.type != Display.TYPE_INTERNAL);
+ if (isEnabled && disallowSecondaryDisplay) {
+ Slog.i(TAG, "Not creating a logical display for a secondary display because single"
+ + " display demo mode is enabled: " + display.getDisplayInfoLocked());
+ isEnabled = false;
+ }
+
+ display.setEnabled(isEnabled);
+ }
+
+ private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) {
+ return isOwnDisplayGroup ? mNextNonDefaultGroupId++ : Display.DEFAULT_DISPLAY_GROUP;
+ }
+
+ private void initializeInternalDisplayDeviceLocked(DisplayDevice device) {
+ // We always want to make sure that our default display layout creates a logical
+ // display for every internal display device that is found.
+ // To that end, when we are notified of a new internal display, we add it to
+ // the default definition if it is not already there.
+ 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, 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/graphics/fonts/FontCrashDetector.java b/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java
deleted file mode 100644
index b082b25aea02..000000000000
--- a/services/core/java/com/android/server/graphics/fonts/FontCrashDetector.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.graphics.fonts;
-
-import android.annotation.NonNull;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * A class to detect font-related native crash.
- *
- * <p>If a fs-verity protected file is accessed through mmap and corrupted file block is detected,
- * SIGBUG signal is generated and the process will crash. To find corrupted files and remove them,
- * we use a marker file to detect crash.
- * <ol>
- * <li>Create a marker file before reading fs-verity protected font files.
- * <li>Delete the marker file after reading font files successfully.
- * <li>If the marker file is found in the next process startup, it means that the process
- * crashed before. We will delete font files to prevent crash loop.
- * </ol>
- *
- * <p>Example usage:
- * <pre>
- * FontCrashDetector detector = new FontCrashDetector(new File("/path/to/marker_file"));
- * if (detector.hasCrashed()) {
- * // Do cleanup
- * }
- * try (FontCrashDetector.MonitoredBlock b = detector.start()) {
- * // Read files
- * }
- * </pre>
- *
- * <p>This class DOES NOT detect Java exceptions. If a Java exception is thrown while monitoring
- * crash, the marker file will be deleted. Creating and deleting marker files are not lightweight.
- * Please use this class sparingly with caution.
- */
-/* package */ final class FontCrashDetector {
-
- private static final String TAG = "FontCrashDetector";
-
- @NonNull
- private final File mMarkerFile;
-
- /* package */ FontCrashDetector(@NonNull File markerFile) {
- mMarkerFile = markerFile;
- }
-
- /* package */ boolean hasCrashed() {
- return mMarkerFile.exists();
- }
-
- /* package */ void clear() {
- if (!mMarkerFile.delete()) {
- Slog.e(TAG, "Could not delete marker file: " + mMarkerFile);
- }
- }
-
- /** Starts crash monitoring. */
- /* package */ MonitoredBlock start() {
- try {
- mMarkerFile.createNewFile();
- } catch (IOException e) {
- Slog.e(TAG, "Could not create marker file: " + mMarkerFile, e);
- }
- return new MonitoredBlock();
- }
-
- /** A helper class to monitor crash with try-with-resources syntax. */
- /* package */ class MonitoredBlock implements AutoCloseable {
- /** Ends crash monitoring. */
- @Override
- public void close() {
- clear();
- }
- }
-}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 01e839dae07a..900ec905609f 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -63,7 +63,6 @@ public final class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
private static final String FONT_FILES_DIR = "/data/fonts/files";
- private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
@Override
public FontConfig getFontConfig() {
@@ -200,10 +199,6 @@ public final class FontManagerService extends IFontManager.Stub {
private final Object mUpdatableFontDirLock = new Object();
@GuardedBy("mUpdatableFontDirLock")
- @NonNull
- private final FontCrashDetector mFontCrashDetector;
-
- @GuardedBy("mUpdatableFontDirLock")
@Nullable
private final UpdatableFontDir mUpdatableFontDir;
@@ -217,7 +212,6 @@ public final class FontManagerService extends IFontManager.Stub {
private FontManagerService(Context context) {
mContext = context;
- mFontCrashDetector = new FontCrashDetector(new File(CRASH_MARKER_FILE));
mUpdatableFontDir = createUpdatableFontDir();
initialize();
}
@@ -244,19 +238,8 @@ public final class FontManagerService extends IFontManager.Stub {
}
return;
}
- if (mFontCrashDetector.hasCrashed()) {
- Slog.i(TAG, "Crash detected. Clearing font updates.");
- try {
- mUpdatableFontDir.clearUpdates();
- } catch (SystemFontException e) {
- Slog.e(TAG, "Failed to clear updates.", e);
- }
- mFontCrashDetector.clear();
- }
- try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
- mUpdatableFontDir.loadFontFileMap();
- updateSerializedFontMap();
- }
+ mUpdatableFontDir.loadFontFileMap();
+ updateSerializedFontMap();
}
}
@@ -286,10 +269,8 @@ public final class FontManagerService extends IFontManager.Stub {
FontManager.RESULT_ERROR_VERSION_MISMATCH,
"The base config version is older than current.");
}
- try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
- mUpdatableFontDir.update(requests);
- updateSerializedFontMap();
- }
+ mUpdatableFontDir.update(requests);
+ updateSerializedFontMap();
}
}
@@ -300,10 +281,8 @@ public final class FontManagerService extends IFontManager.Stub {
"The font updater is disabled.");
}
synchronized (mUpdatableFontDirLock) {
- try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
- mUpdatableFontDir.clearUpdates();
- updateSerializedFontMap();
- }
+ mUpdatableFontDir.clearUpdates();
+ updateSerializedFontMap();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8d6bcadb3e2b..7235a921254d 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) {
@@ -1062,7 +1073,21 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Ignore this message.
return true;
}
- setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
+ boolean tvSystemAudioMode = isSystemAudioControlFeatureEnabled();
+ boolean avrSystemAudioMode = HdmiUtils.parseCommandParamSystemAudioStatus(message);
+ // Set System Audio Mode according to TV's settings.
+ // Handle <System Audio Mode Status> here only when
+ // SystemAudioAutoInitiationAction timeout
+ HdmiDeviceInfo avr = getAvrDeviceInfo();
+ if (avr == null) {
+ setSystemAudioMode(false);
+ } else if (avrSystemAudioMode != tvSystemAudioMode) {
+ addAndStartAction(new SystemAudioActionFromTv(this, avr.getLogicalAddress(),
+ tvSystemAudioMode, null));
+ } else {
+ setSystemAudioMode(tvSystemAudioMode);
+ }
+
return true;
}
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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b4d9b01f716f..115cafedca93 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -221,7 +221,7 @@ public class HdmiControlService extends SystemService {
private int mHdmiCecVolumeControl;
// Make sure HdmiCecConfig is instantiated and the XMLs are read.
- private final HdmiCecConfig mHdmiCecConfig;
+ private HdmiCecConfig mHdmiCecConfig;
/**
* Interface to report send result.
@@ -580,6 +580,11 @@ public class HdmiControlService extends SystemService {
mHdmiCecNetwork = hdmiCecNetwork;
}
+ @VisibleForTesting
+ void setHdmiCecConfig(HdmiCecConfig hdmiCecConfig) {
+ mHdmiCecConfig = hdmiCecConfig;
+ }
+
public HdmiCecNetwork getHdmiCecNetwork() {
return mHdmiCecNetwork;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d41f4c76861e..4e8fcf7bc1c5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -159,6 +159,7 @@ import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.CallbackUtils;
import com.android.internal.inputmethod.IBooleanResultCallback;
+import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IInputBindResultResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodInfoListResultCallback;
@@ -3536,6 +3537,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
boolean didStart = false;
InputBindResult res = null;
+ // We shows the IME when the system allows the IME focused target window to restore the
+ // IME visibility (e.g. switching to the app task when last time the IME is visible).
+ if (isTextEditor && mWindowManagerInternal.shouldRestoreImeVisibility(windowToken)) {
+ if (attribute != null) {
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+ attribute, startInputFlags, startInputReason);
+ showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+ SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
+ } else {
+ res = InputBindResult.NULL_EDITOR_INFO;
+ }
+ return res;
+ }
+
switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
if (!sameWindowFocused && (!isTextEditor || !doAutoShow)) {
@@ -5869,87 +5884,102 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void setImeWindowStatus(int vis, int backDisposition) {
- mImms.setImeWindowStatus(mToken, vis, backDisposition);
+ public void setImeWindowStatus(int vis, int backDisposition,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.setImeWindowStatus(mToken, vis, backDisposition));
}
@BinderThread
@Override
- public void reportStartInput(IBinder startInputToken) {
- mImms.reportStartInput(mToken, startInputToken);
+ public void reportStartInput(IBinder startInputToken, IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.reportStartInput(mToken, startInputToken));
}
@BinderThread
@Override
- public IInputContentUriToken createInputContentUriToken(Uri contentUri,
- String packageName) {
- return mImms.createInputContentUriToken(mToken, contentUri, packageName);
+ public void createInputContentUriToken(Uri contentUri, String packageName,
+ IIInputContentUriTokenResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.createInputContentUriToken(mToken, contentUri, packageName));
}
@BinderThread
@Override
- public void reportFullscreenMode(boolean fullscreen) {
- mImms.reportFullscreenMode(mToken, fullscreen);
+ public void reportFullscreenMode(boolean fullscreen, IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.reportFullscreenMode(mToken, fullscreen));
}
@BinderThread
@Override
- public void setInputMethod(String id) {
- mImms.setInputMethod(mToken, id);
+ public void setInputMethod(String id, IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> mImms.setInputMethod(mToken, id));
}
@BinderThread
@Override
- public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
- mImms.setInputMethodAndSubtype(mToken, id, subtype);
+ public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.setInputMethodAndSubtype(mToken, id, subtype));
}
@BinderThread
@Override
- public void hideMySoftInput(int flags) {
- mImms.hideMySoftInput(mToken, flags);
+ public void hideMySoftInput(int flags, IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> mImms.hideMySoftInput(mToken, flags));
}
@BinderThread
@Override
- public void showMySoftInput(int flags) {
- mImms.showMySoftInput(mToken, flags);
+ public void showMySoftInput(int flags, IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> mImms.showMySoftInput(mToken, flags));
}
@BinderThread
@Override
- public void updateStatusIcon(String packageName, @DrawableRes int iconId) {
- mImms.updateStatusIcon(mToken, packageName, iconId);
+ public void updateStatusIcon(String packageName, @DrawableRes int iconId,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.updateStatusIcon(mToken, packageName, iconId));
}
@BinderThread
@Override
- public boolean switchToPreviousInputMethod() {
- return mImms.switchToPreviousInputMethod(mToken);
+ public void switchToPreviousInputMethod(IBooleanResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> mImms.switchToPreviousInputMethod(mToken));
}
@BinderThread
@Override
- public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
- return mImms.switchToNextInputMethod(mToken, onlyCurrentIme);
+ public void switchToNextInputMethod(boolean onlyCurrentIme,
+ IBooleanResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.switchToNextInputMethod(mToken, onlyCurrentIme));
}
@BinderThread
@Override
- public boolean shouldOfferSwitchingToNextInputMethod() {
- return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
+ public void shouldOfferSwitchingToNextInputMethod(
+ IBooleanResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.shouldOfferSwitchingToNextInputMethod(mToken));
}
@BinderThread
@Override
- public void notifyUserAction() {
- mImms.notifyUserAction(mToken);
+ public void notifyUserAction(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
}
@BinderThread
@Override
- public void applyImeVisibility(IBinder windowToken, boolean setVisible) {
- mImms.applyImeVisibility(mToken, windowToken, setVisible);
+ public void applyImeVisibility(IBinder windowToken, boolean setVisible,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback,
+ () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
}
}
}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index 3ac148ddab1b..c93c4b1f21b7 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -24,7 +24,7 @@ import android.location.IGeocodeProvider;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.server.ServiceWatcher;
+import com.android.server.servicewatcher.ServiceWatcher;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index 6ea4bd2b1d6d..e1c87000ea89 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -25,8 +25,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
-import com.android.server.ServiceWatcher;
-import com.android.server.ServiceWatcher.BoundService;
+import com.android.server.servicewatcher.ServiceWatcher;
+import com.android.server.servicewatcher.ServiceWatcher.BoundService;
/**
* Proxy class to bind GmsCore to the ActivityRecognitionHardware.
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
index bdfa6d7aa67e..c70714932792 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
@@ -29,7 +29,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import com.android.server.ServiceWatcher;
+import com.android.server.servicewatcher.ServiceWatcher;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 05d0aefe7c89..dbd8dd9eff3b 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemClock;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import com.android.server.FgThread;
@@ -55,8 +55,8 @@ public class SystemEmergencyHelper extends EmergencyHelper {
// TODO: this doesn't account for multisim phones
- mTelephonyManager.registerPhoneStateListener(FgThread.getExecutor(),
- new EmergencyCallPhoneStateListener());
+ mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(),
+ new EmergencyCallTelephonyCallback());
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -78,8 +78,8 @@ public class SystemEmergencyHelper extends EmergencyHelper {
|| mTelephonyManager.isInEmergencySmsMode();
}
- private class EmergencyCallPhoneStateListener extends PhoneStateListener implements
- PhoneStateListener.CallStateChangedListener {
+ private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener{
@Override
public void onCallStateChanged(int state, String incomingNumber) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index fef30f9ea811..2aa6f2869afb 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -29,6 +29,8 @@ import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static android.os.PowerWhitelistManager.REASON_LOCATION_PROVIDER;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
@@ -227,7 +229,10 @@ public class LocationProviderManager extends
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setDontSendToRestrictedApps(true);
// allows apps to start a fg service in response to a location PI
- options.setTemporaryAppWhitelistDuration(TEMPORARY_APP_ALLOWLIST_DURATION_MS);
+ options.setTemporaryAppAllowlist(TEMPORARY_APP_ALLOWLIST_DURATION_MS,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_LOCATION_PROVIDER,
+ "");
Intent intent = new Intent().putExtra(KEY_LOCATION_CHANGED,
locationResult.getLastLocation());
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 44b62b3659dc..c86e49bc7948 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -36,9 +36,9 @@ import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
-import com.android.server.ServiceWatcher;
-import com.android.server.ServiceWatcher.BoundService;
import com.android.server.location.provider.AbstractLocationProvider;
+import com.android.server.servicewatcher.ServiceWatcher;
+import com.android.server.servicewatcher.ServiceWatcher.BoundService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 7e00fd69a148..364aa2cb41ae 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -193,6 +193,17 @@ class RebootEscrowManager {
0);
}
+ public int getLoadEscrowDataRetryLimit() {
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+ "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
+ }
+
+ public int getLoadEscrowDataRetryIntervalSeconds() {
+ return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
+ "load_escrow_data_retry_interval_seconds",
+ DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+ }
+
public void reportMetric(boolean success) {
// TODO(b/179105110) design error code; and report the true value for other fields.
FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1,
@@ -251,11 +262,8 @@ class RebootEscrowManager {
List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
Objects.requireNonNull(retryHandler);
- final int retryLimit = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
- "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
- final int retryIntervalInSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
- "load_escrow_data_retry_interval_seconds",
- DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
+ final int retryLimit = mInjector.getLoadEscrowDataRetryLimit();
+ final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds();
if (attemptNumber < retryLimit) {
Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
index 9c471b85eb76..ec80521be2e5 100644
--- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -136,7 +136,7 @@ public class ResumeOnRebootServiceProvider {
}
/** Bind to the service */
- public void bindToService(long timeOut) throws TimeoutException {
+ public void bindToService(long timeOut) throws RemoteException, TimeoutException {
if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
CountDownLatch connectionLatch = new CountDownLatch(1);
Intent intent = new Intent();
@@ -210,27 +210,25 @@ public class ResumeOnRebootServiceProvider {
private void throwTypedException(
ParcelableException exception)
- throws IOException {
- if (exception.getCause() instanceof IOException) {
+ throws IOException, RemoteException {
+ if (exception != null && exception.getCause() instanceof IOException) {
exception.maybeRethrow(IOException.class);
- } else if (exception.getCause() instanceof IllegalStateException) {
- exception.maybeRethrow(IllegalStateException.class);
} else {
- // This should not happen. Wrap the cause in IllegalStateException so that it
- // doesn't disrupt the exception handling
- throw new IllegalStateException(exception.getCause());
+ // Wrap the exception and throw it as a RemoteException.
+ throw new RemoteException(TAG + " wrap/unwrap failed", exception,
+ true /* enableSuppression */, true /* writableStackTrace */);
}
}
private void waitForLatch(CountDownLatch latch, String reason, long timeOut)
- throws TimeoutException {
+ throws RemoteException, TimeoutException {
try {
if (!latch.await(timeOut, TimeUnit.SECONDS)) {
throw new TimeoutException("Latch wait for " + reason + " elapsed");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- throw new IllegalStateException("Latch wait for " + reason + " interrupted");
+ throw new RemoteException("Latch wait for " + reason + " interrupted");
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 7ab8b27075b7..a5763aee6336 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -35,6 +35,7 @@ import android.security.Scrypt;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -906,7 +907,7 @@ public class SyntheticPasswordManager {
if (!tokenMap.containsKey(userId)) {
return Collections.emptySet();
}
- return tokenMap.get(userId).keySet();
+ return new ArraySet<>(tokenMap.get(userId).keySet());
}
public boolean removePendingToken(long handle, int userId) {
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index 87e170ad12df..cb0a668871e0 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
+import android.os.PowerWhitelistManager;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -195,8 +196,9 @@ final class MediaButtonReceiverHolder {
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppWhitelistDuration(
- FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
+ options.setTemporaryAppAllowlist(FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
+ PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerWhitelistManager.REASON_MEDIA_BUTTON, "");
if (mPendingIntent != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 639dda6f8981..23195bbe5d7e 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -93,8 +93,7 @@ public final class MediaMetricsManagerService extends SystemService {
StatsLog.write(statsEvent);
}
- @Override
- public String getSessionId(int userId) {
+ private String getSessionIdInternal(int userId) {
byte[] byteId = new byte[16]; // 128 bits
mSecureRandom.nextBytes(byteId);
String id = Base64.encodeToString(byteId, Base64.DEFAULT);
@@ -102,6 +101,16 @@ public final class MediaMetricsManagerService extends SystemService {
}
@Override
+ public String getPlaybackSessionId(int userId) {
+ return getSessionIdInternal(userId);
+ }
+
+ @Override
+ public String getRecordingSessionId(int userId) {
+ return getSessionIdInternal(userId);
+ }
+
+ @Override
public void reportPlaybackErrorEvent(
String sessionId, PlaybackErrorEvent event, int userId) {
StatsEvent statsEvent = StatsEvent.newBuilder()
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/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 067c5c0e7620..a2f2f98b57c7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -169,7 +169,6 @@ import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkPolicyManager.UidState;
-import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -3164,14 +3163,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- @Override
- @Deprecated
- public NetworkQuotaInfo getNetworkQuotaInfo(NetworkState state) {
- Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
- + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
- return new NetworkQuotaInfo();
- }
-
private void enforceSubscriptionPlanAccess(int subId, int callingUid, String callingPackage) {
// Verify they're not lying about package name
mAppOps.checkPackage(callingUid, callingPackage);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 5b9a11bc5a31..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;
@@ -97,7 +97,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkStack;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
@@ -296,7 +296,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/** Last states of all networks sent from ConnectivityService. */
@GuardedBy("mStatsLock")
@Nullable
- private NetworkState[] mLastNetworkStates = null;
+ private NetworkStateSnapshot[] mLastNetworkStateSnapshots = null;
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
@@ -378,8 +378,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
case MSG_UPDATE_IFACES: {
// If no cached states, ignore.
- if (mLastNetworkStates == null) break;
- updateIfaces(mDefaultNetworks, mLastNetworkStates, mActiveIface);
+ if (mLastNetworkStateSnapshots == null) break;
+ // TODO (b/181642673): Protect mDefaultNetworks from concurrent accessing.
+ updateIfaces(mDefaultNetworks, mLastNetworkStateSnapshots, mActiveIface);
break;
}
case MSG_PERFORM_POLL_REGISTER_ALERT: {
@@ -967,10 +968,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- @Override
public void forceUpdateIfaces(
Network[] defaultNetworks,
- NetworkState[] networkStates,
+ NetworkStateSnapshot[] networkStates,
String activeIface,
UnderlyingNetworkInfo[] underlyingNetworkInfos) {
checkNetworkStackPermission(mContext);
@@ -1248,13 +1248,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void updateIfaces(
Network[] defaultNetworks,
- NetworkState[] networkStates,
+ NetworkStateSnapshot[] snapshots,
String activeIface) {
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
mActiveIface = activeIface;
- updateIfacesLocked(defaultNetworks, networkStates);
+ updateIfacesLocked(defaultNetworks, snapshots);
} finally {
mWakeLock.release();
}
@@ -1262,13 +1262,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
/**
- * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
- * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
+ * Inspect all current {@link NetworkStateSnapshot}s to derive mapping from {@code iface} to
+ * {@link NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
* they are combined under a single {@link NetworkIdentitySet}.
*/
@GuardedBy("mStatsLock")
- private void updateIfacesLocked(@Nullable Network[] defaultNetworks,
- @NonNull NetworkState[] states) {
+ private void updateIfacesLocked(@NonNull Network[] defaultNetworks,
+ @NonNull NetworkStateSnapshot[] snapshots) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
@@ -1283,26 +1283,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// Rebuild active interfaces based on connected networks
mActiveIfaces.clear();
mActiveUidIfaces.clear();
- if (defaultNetworks != null) {
- // Caller is ConnectivityService. Update the list of default networks.
- mDefaultNetworks = defaultNetworks;
- }
+ // Update the list of default networks.
+ mDefaultNetworks = defaultNetworks;
- mLastNetworkStates = states;
+ mLastNetworkStateSnapshots = snapshots;
final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
final ArraySet<String> mobileIfaces = new ArraySet<>();
- for (NetworkState state : states) {
- final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+ for (NetworkStateSnapshot snapshot : snapshots) {
+ final boolean isMobile = isNetworkTypeMobile(snapshot.legacyType);
+ final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.network);
final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
- : getSubTypeForState(state);
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ : getSubTypeForStateSnapshot(snapshot);
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
isDefault, subType);
// Traffic occurring on the base interface is always counted for
// both total usage and UID details.
- final String baseIface = state.linkProperties.getInterfaceName();
+ final String baseIface = snapshot.linkProperties.getInterfaceName();
if (baseIface != null) {
findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
@@ -1312,7 +1310,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// If IMS is metered, then the IMS network usage has already included VT usage.
// VT is considered always metered in framework's layer. If VT is not metered
// per carrier's policy, modem will report 0 usage for VT calls.
- if (state.networkCapabilities.hasCapability(
+ if (snapshot.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
// Copy the identify from IMS one but mark it as metered.
@@ -1358,7 +1356,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// (or non eBPF offloaded) TX they would appear on both, however egress interface
// accounting is explicitly bypassed for traffic from the clat uid.
//
- final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+ final List<LinkProperties> stackedLinks = snapshot.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
if (stackedIface != null) {
@@ -1381,7 +1379,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
* transport types do not actually fill this value.
*/
- private int getSubTypeForState(@NonNull NetworkState state) {
+ private int getSubTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return 0;
}
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 9a9b14c31314..b34611b9cd6f 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -207,35 +207,37 @@ public class DataLoaderManagerService extends SystemService {
@Override
public void onServiceDisconnected(ComponentName arg0) {
Slog.i(TAG, "DataLoader " + mId + " disconnected, but will try to recover");
- callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
- destroy();
+ unbindAndReportDestroyed();
}
@Override
public void onBindingDied(ComponentName name) {
Slog.i(TAG, "DataLoader " + mId + " died");
- callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
- destroy();
+ unbindAndReportDestroyed();
}
@Override
public void onNullBinding(ComponentName name) {
Slog.i(TAG, "DataLoader " + mId + " failed to start");
- callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
- destroy();
+ unbindAndReportDestroyed();
}
@Override
public void binderDied() {
Slog.i(TAG, "DataLoader " + mId + " died");
- callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
- destroy();
+ unbindAndReportDestroyed();
}
IDataLoader getDataLoader() {
return mDataLoader;
}
+ private void unbindAndReportDestroyed() {
+ if (unbind()) {
+ callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED);
+ }
+ }
+
void destroy() {
if (mDataLoader != null) {
try {
@@ -244,11 +246,15 @@ public class DataLoaderManagerService extends SystemService {
}
mDataLoader = null;
}
+ unbind();
+ }
+
+ boolean unbind() {
try {
mContext.unbindService(this);
} catch (Exception ignored) {
}
- remove();
+ return remove();
}
private boolean append() {
@@ -266,12 +272,14 @@ public class DataLoaderManagerService extends SystemService {
}
}
- private void remove() {
+ private boolean remove() {
synchronized (mServiceConnections) {
if (mServiceConnections.get(mId) == this) {
mServiceConnections.remove(mId);
+ return true;
}
}
+ return false;
}
private void callListener(int status) {
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index af2fdf70ca50..b751503907fd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2636,7 +2636,8 @@ public class PackageManagerService extends IPackageManager.Stub
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
+ matchFlags)) {
result.addAll(undefinedList);
// Maybe add one for the other profile.
if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel
@@ -2820,8 +2821,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
result.highestApprovalLevel = Math.max(mDomainVerificationManager
- .approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId),
- result.highestApprovalLevel);
+ .approvalLevelForDomain(ps, intent, resultTargetUser, flags,
+ riTargetUser.targetUserId), result.highestApprovalLevel);
}
if (result != null && result.highestApprovalLevel
<= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
@@ -3067,8 +3068,8 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps.getInstantApp(userId)) {
- if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
- userId)) {
+ if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+ instantApps, flags, userId)) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "Instant app approved for intent; pkg: "
+ packageName);
@@ -3995,8 +3996,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
- if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags,
- userId)) {
+ if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent,
+ resolvedActivities, flags, userId)) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
+ ", approved");
@@ -9575,7 +9576,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = ri.activityInfo.packageName;
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps,
- intent, flags, userId)) {
+ intent, query, flags, userId)) {
return ri;
}
}
@@ -9632,10 +9633,10 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private static boolean hasAnyDomainApproval(
@NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting,
- @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags,
- @UserIdInt int userId) {
- return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId)
- > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
+ @NonNull Intent intent, @NonNull List<ResolveInfo> candidates,
+ @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
+ return manager.approvalLevelForDomain(pkgSetting, intent, candidates, resolveInfoFlags,
+ userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}
/**
@@ -11633,9 +11634,17 @@ public class PackageManagerService extends IPackageManager.Stub
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs =
INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+ // Continue monitoring health and loading progress of active incremental packages
mIncrementalManager.registerHealthListener(parsedPackage.getPath(),
healthCheckParams,
new IncrementalHealthListener(parsedPackage.getPackageName()));
+ final IncrementalStatesCallback incrementalStatesCallback =
+ new IncrementalStatesCallback(parsedPackage.getPackageName(),
+ UserHandle.getUid(UserHandle.ALL, pkgSetting.appId),
+ getInstalledUsers(pkgSetting, UserHandle.USER_ALL));
+ pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
+ mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ new IncrementalProgressListener(parsedPackage.getPackageName()));
}
}
return scanResult.pkgSetting.pkg;
@@ -17984,7 +17993,9 @@ public class PackageManagerService extends IPackageManager.Stub
try {
makeDirRecursive(afterCodeFile.getParentFile(), 0775);
if (onIncremental) {
- mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile);
+ // Just link files here. The stage dir will be removed when the installation
+ // session is completed.
+ mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile);
} else {
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
}
@@ -17993,7 +18004,6 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
- //TODO(b/136132412): enable selinux restorecon for incremental directories
if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
Slog.w(TAG, "Failed to restorecon");
return false;
@@ -19419,6 +19429,8 @@ public class PackageManagerService extends IPackageManager.Stub
mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
// Unregister health listener as it will always be healthy from now
mIncrementalManager.unregisterHealthListener(codePath);
+ // Make sure the information is preserved
+ scheduleWriteSettingsLocked();
}
@Override
@@ -19481,11 +19493,11 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.getPackageLPr(mPackageName);
+ if (ps == null) {
+ return;
+ }
+ ps.setLoadingProgress(progress);
}
- if (ps == null) {
- return;
- }
- ps.setLoadingProgress(progress);
}
}
@@ -20089,7 +20101,7 @@ public class PackageManagerService extends IPackageManager.Stub
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Error deriving application ABI");
+ "Error deriving application ABI: " + pme.getMessage());
}
}
@@ -20356,6 +20368,11 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
+ if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
+ < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
+ return;
+ }
+
// Collect files we care for fs-verity setup.
ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
if (legacyMode) {
@@ -26117,6 +26134,11 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManagerService.this.hasSigningCertificate(
packageName, certificate, CERT_INPUT_SHA256);
}
+
+ @Override
+ public boolean hasSystemFeature(String featureName, int version) {
+ return PackageManagerService.this.hasSystemFeature(featureName, version);
+ }
}
private AndroidPackage getPackage(String packageName) {
diff --git a/services/core/java/com/android/server/pm/SettingsXml.java b/services/core/java/com/android/server/pm/SettingsXml.java
index ec643f598041..c53fef72db9c 100644
--- a/services/core/java/com/android/server/pm/SettingsXml.java
+++ b/services/core/java/com/android/server/pm/SettingsXml.java
@@ -83,7 +83,7 @@ public class SettingsXml {
@Override
public void close() throws IOException {
mWriteSection.closeCompletely();
- mXmlSerializer.endDocument();
+ mXmlSerializer.flush();
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 1c5f0a7fc0f3..f411c98433cf 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -280,7 +280,8 @@ public class ShortcutBitmapSaver {
IoUtils.closeQuietly(out);
}
- shortcut.setBitmapPath(file.getAbsolutePath());
+ final String path = file.getAbsolutePath();
+ mService.postValue(shortcut, si -> si.setBitmapPath(path));
} catch (IOException | RuntimeException e) {
Slog.e(ShortcutService.TAG, "Unable to write bitmap to file", e);
@@ -295,12 +296,14 @@ public class ShortcutBitmapSaver {
Slog.d(TAG, "Saved bitmap.");
}
if (shortcut != null) {
- if (shortcut.getBitmapPath() == null) {
- removeIcon(shortcut);
- }
+ mService.postValue(shortcut, si -> {
+ if (si.getBitmapPath() == null) {
+ removeIcon(si);
+ }
- // Whatever happened, remove this flag.
- shortcut.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ // Whatever happened, remove this flag.
+ si.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ });
}
}
return true;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index a604afc22c09..302e6572a245 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -223,12 +223,14 @@ class ShortcutPackage extends ShortcutPackageItem {
// - Disable if needed.
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
ShortcutInfo si = mShortcuts.valueAt(i);
- si.clearFlags(ShortcutInfo.FLAG_SHADOW);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.clearFlags(ShortcutInfo.FLAG_SHADOW);
- si.setDisabledReason(restoreBlockReason);
- if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- si.addFlags(ShortcutInfo.FLAG_DISABLED);
- }
+ shortcut.setDisabledReason(restoreBlockReason);
+ if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+ shortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
+ }
+ });
}
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
@@ -460,9 +462,11 @@ class ShortcutPackage extends ShortcutPackageItem {
if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
changed = true;
- si.setTimestamp(now);
- si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
- si.setRank(0); // It may still be pinned, so clear the rank.
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ shortcut.setRank(0); // It may still be pinned, so clear the rank.
+ });
}
}
if (changed) {
@@ -506,7 +510,7 @@ class ShortcutPackage extends ShortcutPackageItem {
public ShortcutInfo deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
if (shortcut != null) {
- shortcut.clearFlags(ShortcutInfo.FLAG_CACHED_ALL);
+ mutateShortcut(shortcutId, null, si -> si.clearFlags(ShortcutInfo.FLAG_CACHED_ALL));
}
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
@@ -527,15 +531,16 @@ class ShortcutPackage extends ShortcutPackageItem {
overrideImmutable, ignoreInvisible, disabledReason);
// If disabled id still exists, it is pinned and we need to update the disabled message.
- final ShortcutInfo disabled = mShortcuts.get(shortcutId);
- if (disabled != null) {
- if (disabledMessage != null) {
- disabled.setDisabledMessage(disabledMessage);
- } else if (disabledMessageResId != 0) {
- disabled.setDisabledMessageResId(disabledMessageResId);
- mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
+ mutateShortcut(shortcutId, null, disabled -> {
+ if (disabled != null) {
+ if (disabledMessage != null) {
+ disabled.setDisabledMessage(disabledMessage);
+ } else if (disabledMessageResId != 0) {
+ disabled.setDisabledMessageResId(disabledMessageResId);
+ mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
+ }
}
- }
+ });
return deleted;
}
@@ -557,21 +562,23 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (oldShortcut.isPinned() || oldShortcut.isCached()) {
- oldShortcut.setRank(0);
- oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
- if (disable) {
- oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
- // Do not overwrite the disabled reason if one is alreay set.
- if (oldShortcut.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
- oldShortcut.setDisabledReason(disabledReason);
+ mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
+ si.setRank(0);
+ si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
+ if (disable) {
+ si.addFlags(ShortcutInfo.FLAG_DISABLED);
+ // Do not overwrite the disabled reason if one is alreay set.
+ if (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
+ si.setDisabledReason(disabledReason);
+ }
}
- }
- oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
+ si.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis());
- // See ShortcutRequestPinProcessor.directPinShortcut().
- if (mShortcutUser.mService.isDummyMainActivity(oldShortcut.getActivity())) {
- oldShortcut.setActivity(null);
- }
+ // See ShortcutRequestPinProcessor.directPinShortcut().
+ if (mShortcutUser.mService.isDummyMainActivity(si.getActivity())) {
+ si.setActivity(null);
+ }
+ });
return null;
} else {
@@ -581,12 +588,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
public void enableWithId(@NonNull String shortcutId) {
- final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
- if (shortcut != null) {
- ensureNotImmutable(shortcut, /*ignoreInvisible=*/ true);
- shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
- shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- }
+ mutateShortcut(shortcutId, null, si -> {
+ ensureNotImmutable(si, /*ignoreInvisible=*/ true);
+ si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
@@ -609,22 +615,25 @@ class ShortcutPackage extends ShortcutPackageItem {
* <p>Then remove all shortcuts that are not dynamic and no longer pinned either.
*/
public void refreshPinnedFlags() {
- // First, un-pin all shortcuts
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
+ // TODO: rewrite this function with proper query (i.e. fetch only pinned shortcuts and
+ // unpin if it's no longer pinned by any launcher and vice versa)
+ final List<ShortcutInfo> shortcuts = new ArrayList<>(mShortcuts.values());
+ final Map<String, ShortcutInfo> shortcutMap = new ArrayMap<>(shortcuts.size());
+ for (ShortcutInfo si : shortcuts) {
+ shortcutMap.put(si.getId(), si);
}
+ final Set<String> pinnedShortcuts = new ArraySet<>();
- // Then, for the pinned set for each launcher, set the pin flag one by one.
+ // First, for the pinned set for each launcher, keep track of their id one by one.
mShortcutUser.forAllLaunchers(launcherShortcuts -> {
final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
getPackageName(), getPackageUserId());
-
if (pinned == null || pinned.size() == 0) {
return;
}
for (int i = pinned.size() - 1; i >= 0; i--) {
final String id = pinned.valueAt(i);
- final ShortcutInfo si = mShortcuts.get(id);
+ final ShortcutInfo si = shortcutMap.get(id);
if (si == null) {
// This happens if a launcher pinned shortcuts from this package, then backup&
// restored, but this package doesn't allow backing up.
@@ -632,9 +641,21 @@ class ShortcutPackage extends ShortcutPackageItem {
// That's fine, when the launcher is restored, we'll fix it.
continue;
}
- si.addFlags(ShortcutInfo.FLAG_PINNED);
+ pinnedShortcuts.add(si.getId());
}
});
+ // Then, update the pinned state if necessary
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = shortcuts.get(i);
+ if (pinnedShortcuts.contains(si.getId()) && !si.isPinned()) {
+ mutateShortcut(si.getId(), si,
+ shortcut -> shortcut.addFlags(ShortcutInfo.FLAG_PINNED));
+ }
+ if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
+ mutateShortcut(si.getId(), si, shortcut ->
+ shortcut.clearFlags(ShortcutInfo.FLAG_PINNED));
+ }
+ }
// Lastly, remove the ones that are no longer pinned, cached nor dynamic.
removeOrphans();
@@ -1034,8 +1055,10 @@ class ShortcutPackage extends ShortcutPackageItem {
continue;
}
Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
- si.clearFlags(ShortcutInfo.FLAG_DISABLED);
- si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
// For existing shortcuts, update timestamps if they have any resources.
@@ -1065,21 +1088,24 @@ class ShortcutPackage extends ShortcutPackageItem {
}
if (si.hasAnyResources()) {
- if (!si.isOriginallyFromManifest()) {
+ if (publisherRes == null) {
+ publisherRes = getPackageResources();
if (publisherRes == null) {
- publisherRes = getPackageResources();
- if (publisherRes == null) {
- break; // Resources couldn't be loaded.
- }
+ break; // Resources couldn't be loaded.
+ }
+ }
+
+ final Resources res = publisherRes;
+ mutateShortcut(si.getId(), si, shortcut -> {
+ if (!shortcut.isOriginallyFromManifest()) {
+ shortcut.lookupAndFillInResourceIds(res);
}
- // TODO: update resource strings in AppSearch
// If this shortcut is not from a manifest, then update all resource IDs
// from resource names. (We don't allow resource strings for
// non-manifest at the moment, but icons can still be resources.)
- si.lookupAndFillInResourceIds(publisherRes);
- }
- si.setTimestamp(s.injectCurrentTimeMillis());
+ shortcut.setTimestamp(s.injectCurrentTimeMillis());
+ });
}
}
}
@@ -1382,8 +1408,11 @@ class ShortcutPackage extends ShortcutPackageItem {
}
}
- si.resolveResourceStrings(publisherRes);
- si.setTimestamp(s.injectCurrentTimeMillis());
+ final Resources res = publisherRes;
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.resolveResourceStrings(res);
+ shortcut.setTimestamp(s.injectCurrentTimeMillis());
+ });
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -1400,7 +1429,7 @@ class ShortcutPackage extends ShortcutPackageItem {
public void clearAllImplicitRanks() {
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- si.clearImplicitRankAndRankChangedFlag();
+ mutateShortcut(si.getId(), si, ShortcutInfo::clearImplicitRankAndRankChangedFlag);
}
}
@@ -1445,8 +1474,10 @@ class ShortcutPackage extends ShortcutPackageItem {
final ShortcutInfo si = mShortcuts.valueAt(i);
if (si.isFloating()) {
if (si.getRank() != 0) {
- si.setTimestamp(now);
- si.setRank(0);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.setRank(0);
+ });
}
}
}
@@ -1479,8 +1510,10 @@ class ShortcutPackage extends ShortcutPackageItem {
}
final int thisRank = rank++;
if (si.getRank() != thisRank) {
- si.setTimestamp(now);
- si.setRank(thisRank);
+ mutateShortcut(si.getId(), si, shortcut -> {
+ shortcut.setTimestamp(now);
+ shortcut.setRank(thisRank);
+ });
}
}
}
@@ -2172,6 +2205,32 @@ class ShortcutPackage extends ShortcutPackageItem {
resetAppSearch(null);
}
+ void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
+ @NonNull final Consumer<ShortcutInfo> transform) {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(transform);
+ synchronized (mLock) {
+ if (shortcut != null) {
+ transform.accept(shortcut);
+ } else {
+ transform.accept(findShortcutById(id));
+ }
+ // TODO: Load ShortcutInfo from AppSearch, apply transformation logic and save
+ }
+ }
+
+ /**
+ * Removes shortcuts from AppSearch.
+ */
+ void removeShortcuts() {
+ }
+
+ /**
+ * Merge/replace shortcuts parsed from xml file.
+ */
+ void restoreParsedShortcuts(final boolean replace) {
+ }
+
private boolean verifyRanksSequential(List<ShortcutInfo> list) {
boolean failed = false;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 209a143f665d..d1cf55de7254 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -103,6 +103,7 @@ import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
@@ -142,6 +143,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -362,6 +364,7 @@ public class ShortcutService extends IShortcutService.Stub {
private List<Integer> mDirtyUserIds = new ArrayList<>();
private final AtomicBoolean mBootCompleted = new AtomicBoolean();
+ private final AtomicBoolean mShutdown = new AtomicBoolean();
/**
* Note we use a fine-grained lock for {@link #mUnlockedUsers} due to b/64303666.
@@ -498,6 +501,12 @@ public class ShortcutService extends IShortcutService.Stub {
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL,
localeFilter, null, mHandler);
+ IntentFilter shutdownFilter = new IntentFilter();
+ shutdownFilter.addAction(Intent.ACTION_SHUTDOWN);
+ shutdownFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiverAsUser(mShutdownReceiver, UserHandle.SYSTEM,
+ shutdownFilter, null, mHandler);
+
injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_GONE);
@@ -662,7 +671,7 @@ public class ShortcutService extends IShortcutService.Stub {
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mUnlockedUsers) {
mUnlockedUsers.put(userId, true);
@@ -1162,6 +1171,9 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG) {
Slog.d(TAG, "saveDirtyInfo");
}
+ if (mShutdown.get()) {
+ return;
+ }
try {
synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
@@ -1179,6 +1191,14 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ void postValue(@NonNull final ShortcutInfo shortcutInfo,
+ @NonNull final Consumer<ShortcutInfo> cb) {
+ final String pkg = shortcutInfo.getPackage();
+ final int userId = shortcutInfo.getUserId();
+ final String id = shortcutInfo.getId();
+ getPackageShortcutsLocked(pkg, userId).mutateShortcut(id, shortcutInfo, cb);
+ }
+
/** Return the last reset time. */
@GuardedBy("mLock")
long getLastResetTimeLocked() {
@@ -1566,7 +1586,6 @@ public class ShortcutService extends IShortcutService.Stub {
* resource-based strings.
*/
void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
- // TODO: update resource names in AppSearch
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
@@ -1868,8 +1887,8 @@ public class ShortcutService extends IShortcutService.Stub {
// === APIs ===
@Override
- public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
- @UserIdInt int userId) {
+ public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ @UserIdInt int userId, @NonNull AndroidFuture callback) {
verifyCaller(packageName, userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -1896,7 +1915,7 @@ public class ShortcutService extends IShortcutService.Stub {
// Throttling.
if (!ps.tryApiCall(unlimited)) {
- return false;
+ callback.complete(false);
}
// Initialize the implicit ranks for ShortcutPackage.adjustRanks().
@@ -1932,12 +1951,12 @@ public class ShortcutService extends IShortcutService.Stub {
verifyStates();
- return true;
+ callback.complete(true);
}
@Override
- public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
- @UserIdInt int userId) {
+ public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ @UserIdInt int userId, AndroidFuture callback) {
verifyCaller(packageName, userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -1947,7 +1966,7 @@ public class ShortcutService extends IShortcutService.Stub {
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
- List<ShortcutInfo> changedShortcuts = null;
+ final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -1964,7 +1983,8 @@ public class ShortcutService extends IShortcutService.Stub {
// Throttling.
if (!ps.tryApiCall(unlimited)) {
- return false;
+ callback.complete(false);
+ return;
}
// Initialize the implicit ranks for ShortcutPackage.adjustRanks().
@@ -1975,68 +1995,66 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutInfo source = newShortcuts.get(i);
fixUpIncomingShortcutInfo(source, /* forUpdate= */ true);
- final ShortcutInfo target = ps.findShortcutById(source.getId());
-
- // Invisible shortcuts can't be updated.
- if (target == null || !target.isVisibleToPublisher()) {
- continue;
- }
+ ps.mutateShortcut(source.getId(), null, target -> {
+ // Invisible shortcuts can't be updated.
+ if (target == null || !target.isVisibleToPublisher()) {
+ return;
+ }
- if (target.isEnabled() != source.isEnabled()) {
- Slog.w(TAG,
- "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
- }
+ if (target.isEnabled() != source.isEnabled()) {
+ Slog.w(TAG,
+ "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
+ }
- if (target.isLongLived() != source.isLongLived()) {
- Slog.w(TAG,
- "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
- }
+ if (target.isLongLived() != source.isLongLived()) {
+ Slog.w(TAG,
+ "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
+ }
- // When updating the rank, we need to insert between existing ranks, so set
- // this setRankChanged, and also copy the implicit rank fo adjustRanks().
- if (source.hasRank()) {
- target.setRankChanged();
- target.setImplicitRank(source.getImplicitRank());
- }
+ // When updating the rank, we need to insert between existing ranks, so set
+ // this setRankChanged, and also copy the implicit rank fo adjustRanks().
+ if (source.hasRank()) {
+ target.setRankChanged();
+ target.setImplicitRank(source.getImplicitRank());
+ }
- final boolean replacingIcon = (source.getIcon() != null);
- if (replacingIcon) {
- removeIconLocked(target);
- }
+ final boolean replacingIcon = (source.getIcon() != null);
+ if (replacingIcon) {
+ removeIconLocked(target);
+ }
- // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
- target.copyNonNullFieldsFrom(source);
- target.setTimestamp(injectCurrentTimeMillis());
+ // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
+ target.copyNonNullFieldsFrom(source);
+ target.setTimestamp(injectCurrentTimeMillis());
- if (replacingIcon) {
- saveIconAndFixUpShortcutLocked(target);
- }
+ if (replacingIcon) {
+ saveIconAndFixUpShortcutLocked(target);
+ }
- // When we're updating any resource related fields, re-extract the res names and
- // the values.
- if (replacingIcon || source.hasStringResources()) {
- fixUpShortcutResourceNamesAndValues(target);
- }
+ // When we're updating any resource related fields, re-extract the res names and
+ // the values.
+ if (replacingIcon || source.hasStringResources()) {
+ fixUpShortcutResourceNamesAndValues(target);
+ }
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(target);
+ changedShortcuts.add(target);
+ });
}
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId, changedShortcuts, null);
+ packageShortcutsChanged(packageName, userId,
+ changedShortcuts.isEmpty() ? null : changedShortcuts, null);
verifyStates();
- return true;
+ callback.complete(true);
}
@Override
- public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
- @UserIdInt int userId) {
+ public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ @UserIdInt int userId, AndroidFuture callback) {
verifyCaller(packageName, userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
@@ -2066,7 +2084,8 @@ public class ShortcutService extends IShortcutService.Stub {
// Throttling.
if (!ps.tryApiCall(unlimited)) {
- return false;
+ callback.complete(false);
+ return;
}
for (int i = 0; i < size; i++) {
final ShortcutInfo newShortcut = newShortcuts.get(i);
@@ -2094,7 +2113,7 @@ public class ShortcutService extends IShortcutService.Stub {
verifyStates();
- return true;
+ callback.complete(true);
}
@Override
@@ -2159,15 +2178,17 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
- public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
- IntentSender resultIntent, int userId) {
+ public void requestPinShortcut(String packageName, ShortcutInfo shortcut,
+ IntentSender resultIntent, int userId, AndroidFuture callback) {
Objects.requireNonNull(shortcut);
+ Objects.requireNonNull(callback);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
- return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
+ callback.complete(requestPinItem(packageName, userId, shortcut, null, null, resultIntent));
}
@Override
- public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
+ public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId,
+ AndroidFuture callback)
throws RemoteException {
Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
@@ -2183,7 +2204,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
verifyStates();
- return ret;
+ callback.complete(ret);
}
/**
@@ -2440,8 +2461,9 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
- public ParceledListSlice<ShortcutInfo> getShortcuts(String packageName,
- @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) {
+ public void getShortcuts(String packageName,
+ @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId,
+ AndroidFuture<ParceledListSlice<ShortcutInfo>> callback) {
verifyCaller(packageName, userId);
synchronized (mLock) {
@@ -2457,16 +2479,16 @@ public class ShortcutService extends IShortcutService.Stub {
| (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
| (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
- return getShortcutsWithQueryLocked(
+ callback.complete(getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
(ShortcutInfo si) ->
- si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0);
+ si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0));
}
}
@Override
- public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName,
- IntentFilter filter, @UserIdInt int userId) {
+ public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId,
+ AndroidFuture<ParceledListSlice> callback) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
Objects.requireNonNull(filter, "intentFilter");
@@ -2482,7 +2504,7 @@ public class ShortcutService extends IShortcutService.Stub {
final ShortcutUser user = getUserShortcutsLocked(userId);
user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter)));
- return new ParceledListSlice<>(shortcutInfoList);
+ callback.complete(new ParceledListSlice<>(shortcutInfoList));
}
}
@@ -3066,8 +3088,14 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public List<ShortcutManager.ShareShortcutInfo> getShareTargets(
@NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) {
- return ShortcutService.this.getShareTargets(
- callingPackage, intentFilter, userId).getList();
+ final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>();
+ ShortcutService.this.getShareTargets(
+ callingPackage, intentFilter, userId, future);
+ try {
+ return future.get().getList();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
@@ -3114,7 +3142,8 @@ public class ShortcutService extends IShortcutService.Stub {
if (doCache) {
if (si.isLongLived()) {
- si.addFlags(cacheFlags);
+ sp.mutateShortcut(si.getId(), si,
+ shortcut -> shortcut.addFlags(cacheFlags));
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
@@ -3125,7 +3154,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
} else {
ShortcutInfo removed = null;
- si.clearFlags(cacheFlags);
+ sp.mutateShortcut(si.getId(), si, shortcut ->
+ shortcut.clearFlags(cacheFlags));
if (!si.isDynamic() && !si.isCached()) {
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
}
@@ -3487,6 +3517,22 @@ public class ShortcutService extends IShortcutService.Stub {
}
};
+ private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Since it cleans up the shortcut directory and rewrite the ShortcutPackageItems
+ // in odrder during saveToXml(), it could lead to shortcuts missing when shutdown.
+ // We need it so that it can finish up saving before shutdown.
+ synchronized (mLock) {
+ if (mHandler.hasCallbacks(mSaveDirtyInfoRunner)) {
+ mHandler.removeCallbacks(mSaveDirtyInfoRunner);
+ saveDirtyInfo();
+ }
+ mShutdown.set(true);
+ }
+ }
+ };
+
/**
* Called when a user is unlocked.
* - Check all known packages still exist, and otherwise perform cleanup.
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 6cbc47fb59c4..ec784d0211dd 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -185,6 +185,9 @@ class ShortcutUser {
public ShortcutPackage removePackage(@NonNull String packageName) {
final ShortcutPackage removed = mPackages.remove(packageName);
+ if (removed != null) {
+ removed.removeShortcuts();
+ }
mService.cleanupBitmapsForPackage(mUserId, packageName);
return removed;
@@ -330,7 +333,10 @@ class ShortcutUser {
if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
if (isNewApp) {
- mPackages.remove(packageName);
+ final ShortcutPackage sp = mPackages.remove(packageName);
+ if (sp != null) {
+ sp.removeShortcuts();
+ }
}
}
}
@@ -454,6 +460,7 @@ class ShortcutUser {
case ShortcutPackage.TAG_ROOT: {
final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
s, ret, parser, fromBackup);
+ shortcuts.restoreParsedShortcuts(false);
// Don't use addShortcut(), we don't need to save the icon.
ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
@@ -488,6 +495,7 @@ class ShortcutUser {
final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup);
if (sp != null) {
ret.mPackages.put(sp.getPackageName(), sp);
+ sp.restoreParsedShortcuts(false);
}
});
@@ -570,6 +578,7 @@ class ShortcutUser {
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
+ sp.restoreParsedShortcuts(true);
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 871576e7c795..8283ac668d5e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1544,7 +1544,7 @@ public class UserManagerService extends IUserManager.Stub {
public String getUserName() {
final int callingUid = Binder.getCallingUid();
if (!hasManageOrCreateUsersPermission()
- || hasPermissionGranted(
+ && !hasPermissionGranted(
android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
+ "GET_ACCOUNTS_PRIVILEGED permissions to: get user name");
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index e05ef482ec08..8c1a90c13513 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -1,9 +1,7 @@
-zhanghai@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
per-file DefaultPermissionGrantPolicy.java = hackbod@android.com
per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com
-per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
per-file DefaultPermissionGrantPolicy.java = toddke@google.com
per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
per-file DefaultPermissionGrantPolicy.java = patb@google.com
-per-file DefaultPermissionGrantPolicy.java = eugenesusla@google.com
-per-file DefaultPermissionGrantPolicy.java = zhanghai@google.com
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index e3cf67c34dad..bf2b3c7f491f 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -84,7 +84,7 @@ public class DomainVerificationCollector {
@NonNull
public ArraySet<String> collectAllWebDomains(@NonNull AndroidPackage pkg) {
- return collectDomains(pkg, false);
+ return collectDomains(pkg, false /* checkAutoVerify */, true /* valid */);
}
/**
@@ -92,20 +92,29 @@ public class DomainVerificationCollector {
* IntentFilter#getAutoVerify()} == true.
*/
@NonNull
- public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) {
- return collectDomains(pkg, true);
+ public ArraySet<String> collectValidAutoVerifyDomains(@NonNull AndroidPackage pkg) {
+ return collectDomains(pkg, true /* checkAutoVerify */, true /* valid */);
+ }
+
+ /**
+ * Returns all the domains that are configured to be auto verified, but aren't actually valid
+ * HTTP domains, per {@link #DOMAIN_NAME_WITH_WILDCARD}.
+ */
+ @NonNull
+ public ArraySet<String> collectInvalidAutoVerifyDomains(@NonNull AndroidPackage pkg) {
+ return collectDomains(pkg, true /* checkAutoVerify */, false /* valid */);
}
@NonNull
private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg,
- boolean checkAutoVerify) {
+ boolean checkAutoVerify, boolean valid) {
boolean restrictDomains =
DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);
if (restrictDomains) {
- return collectDomainsInternal(pkg, checkAutoVerify);
+ return collectDomainsInternal(pkg, checkAutoVerify, valid);
} else {
- return collectDomainsLegacy(pkg, checkAutoVerify);
+ return collectDomainsLegacy(pkg, checkAutoVerify, valid);
}
}
@@ -113,10 +122,10 @@ public class DomainVerificationCollector {
* @see #RESTRICT_DOMAINS
*/
private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg,
- boolean checkAutoVerify) {
+ boolean checkAutoVerify, boolean valid) {
if (!checkAutoVerify) {
// Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
- return collectDomainsInternal(pkg, false);
+ return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */);
}
List<ParsedActivity> activities = pkg.getActivities();
@@ -157,7 +166,7 @@ public class DomainVerificationCollector {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount; index++) {
String host = intent.getDataAuthority(index).getHost();
- if (isValidHost(host)) {
+ if (isValidHost(host) == valid) {
totalSize += byteSizeOf(host);
underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
domains.add(host);
@@ -174,7 +183,7 @@ public class DomainVerificationCollector {
* @see #RESTRICT_DOMAINS
*/
private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg,
- boolean checkAutoVerify) {
+ boolean checkAutoVerify, boolean valid) {
ArraySet<String> domains = new ArraySet<>();
int totalSize = 0;
boolean underMaxSize = true;
@@ -214,7 +223,7 @@ public class DomainVerificationCollector {
int authorityCount = intent.countDataAuthorities();
for (int index = 0; index < authorityCount && underMaxSize; index++) {
String host = intent.getDataAuthority(index).getHost();
- if (isValidHost(host)) {
+ if (isValidHost(host) == valid) {
totalSize += byteSizeOf(host);
underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
domains.add(host);
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 b3108c58a11e..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;
@@ -107,7 +107,7 @@ public class DomainVerificationDebug {
reusedMap.clear();
reusedMap.putAll(pkgState.getStateMap());
- ArraySet<String> declaredDomains = mCollector.collectAutoVerifyDomains(pkg);
+ ArraySet<String> declaredDomains = mCollector.collectValidAutoVerifyDomains(pkg);
int declaredSize = declaredDomains.size();
for (int declaredIndex = 0; declaredIndex < declaredSize; declaredIndex++) {
String domain = declaredDomains.valueAt(declaredIndex);
@@ -132,6 +132,17 @@ public class DomainVerificationDebug {
}
writer.increaseIndent();
+ final ArraySet<String> invalidDomains = mCollector.collectInvalidAutoVerifyDomains(pkg);
+ if (!invalidDomains.isEmpty()) {
+ writer.println("Invalid autoVerify domains:");
+ writer.increaseIndent();
+ int size = invalidDomains.size();
+ for (int index = 0; index < size; index++) {
+ writer.println(invalidDomains.valueAt(index));
+ }
+ writer.decreaseIndent();
+ }
+
writer.println("Domain verification state:");
writer.increaseIndent();
int stateSize = reusedMap.size();
@@ -158,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) {
@@ -167,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);
}
@@ -181,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/DomainVerificationLegacySettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
index c787356f342c..4bad1020e945 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationLegacySettings.java
@@ -32,7 +32,6 @@ import com.android.server.pm.SettingsXml;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.Map;
/**
* Reads and writes the old {@link android.content.pm.IntentFilterVerificationInfo} so that it can
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 9e22d82910df..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.
*/
/*
@@ -310,6 +323,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan
*/
@ApprovalLevel
int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ @NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId);
/**
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 b58c1ff374d5..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) {
@@ -256,7 +256,7 @@ public class DomainVerificationService extends SystemService
Map<String, Integer> hostToStateMap = new ArrayMap<>(pkgState.getStateMap());
// TODO(b/159952358): Should the domain list be cached?
- ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
if (domains.isEmpty()) {
return null;
}
@@ -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));
}
}
@@ -354,7 +353,8 @@ public class DomainVerificationService extends SystemService
validDomains.clear();
- ArraySet<String> autoVerifyDomains = mCollector.collectAutoVerifyDomains(pkg);
+ ArraySet<String> autoVerifyDomains =
+ mCollector.collectValidAutoVerifyDomains(pkg);
if (domains == null) {
validDomains.addAll(autoVerifyDomains);
} else {
@@ -379,9 +379,9 @@ public class DomainVerificationService extends SystemService
AndroidPackage pkg = pkgSetting.getPkg();
if (domains == null) {
- domains = mCollector.collectAutoVerifyDomains(pkg);
+ domains = mCollector.collectValidAutoVerifyDomains(pkg);
} else {
- domains.retainAll(mCollector.collectAutoVerifyDomains(pkg));
+ domains.retainAll(mCollector.collectValidAutoVerifyDomains(pkg));
}
setDomainVerificationStatusInternal(pkgState, state, domains);
@@ -400,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);
@@ -414,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(),
@@ -433,7 +426,7 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserSelectionState(userId)
+ pkgState.getOrCreateUserState(userId)
.setLinkHandlingAllowed(allowed);
}
@@ -451,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);
}
}
@@ -467,7 +460,7 @@ public class DomainVerificationService extends SystemService
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- pkgState.getOrCreateUserSelectionState(userId)
+ pkgState.getOrCreateUserState(userId)
.setLinkHandlingAllowed(allowed);
}
}
@@ -475,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 {
@@ -499,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.
@@ -539,8 +525,8 @@ public class DomainVerificationService extends SystemService
continue;
}
- DomainVerificationUserState approvedUserState =
- approvedPkgState.getUserSelectionState(userId);
+ DomainVerificationInternalUserState approvedUserState =
+ approvedPkgState.getUserState(userId);
if (approvedUserState == null) {
continue;
}
@@ -622,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 {
@@ -631,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 {
@@ -642,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);
}
@@ -672,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++) {
@@ -681,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);
@@ -693,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);
@@ -794,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
@@ -811,7 +784,8 @@ public class DomainVerificationService extends SystemService
}
ArrayMap<String, Integer> oldStateMap = oldPkgState.getStateMap();
- ArraySet<String> newAutoVerifyDomains = mCollector.collectAutoVerifyDomains(newPkg);
+ ArraySet<String> newAutoVerifyDomains =
+ mCollector.collectValidAutoVerifyDomains(newPkg);
int newDomainsSize = newAutoVerifyDomains.size();
for (int newDomainsIndex = 0; newDomainsIndex < newDomainsSize; newDomainsIndex++) {
@@ -836,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.collectAutoVerifyDomains(newPkg);
+ 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);
}
}
@@ -893,7 +868,7 @@ public class DomainVerificationService extends SystemService
}
AndroidPackage pkg = newPkgSetting.getPkg();
- ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
boolean hasAutoVerifyDomains = !domains.isEmpty();
boolean isPendingOrRestored = pkgState != null;
if (isPendingOrRestored) {
@@ -924,7 +899,7 @@ public class DomainVerificationService extends SystemService
webDomains = mCollector.collectAllWebDomains(pkg);
}
- pkgState.getOrCreateUserSelectionState(userId).addHosts(webDomains);
+ pkgState.getOrCreateUserState(userId).addHosts(webDomains);
}
}
@@ -1157,7 +1132,7 @@ public class DomainVerificationService extends SystemService
}
AndroidPackage pkg = pkgSetting.getPkg();
ArraySet<String> declaredDomains = forAutoVerify
- ? mCollector.collectAutoVerifyDomains(pkg)
+ ? mCollector.collectValidAutoVerifyDomains(pkg)
: mCollector.collectAllWebDomains(pkg);
if (domains.retainAll(declaredDomains)) {
@@ -1289,11 +1264,11 @@ public class DomainVerificationService extends SystemService
}
}
- applyImmutableState(pkgState, mCollector.collectAutoVerifyDomains(pkg));
+ applyImmutableState(pkgState, mCollector.collectValidAutoVerifyDomains(pkg));
}
@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) {
@@ -1494,9 +1469,11 @@ public class DomainVerificationService extends SystemService
@Override
public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
+ @NonNull List<ResolveInfo> candidates,
@PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
String packageName = pkgSetting.getName();
- if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
+ if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates,
+ resolveInfoFlags)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, intent, userId, false, "not valid intent");
}
@@ -1541,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) {
@@ -1557,7 +1534,7 @@ public class DomainVerificationService extends SystemService
// To allow an instant app to immediately open domains after being installed by the
// user, auto approve them for any declared autoVerify domains.
if (pkgSetting.getInstantApp(userId)
- && mCollector.collectAutoVerifyDomains(pkg).contains(host)) {
+ && mCollector.collectValidAutoVerifyDomains(pkg).contains(host)) {
return APPROVAL_LEVEL_INSTANT_APP;
}
}
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/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 783aff6ccb55..883bbad1bd2d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,12 +22,17 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
+import com.android.internal.util.CollectionUtils;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import java.util.List;
+import java.util.Set;
+
public final class DomainVerificationUtils {
/**
@@ -40,13 +45,61 @@ public final class DomainVerificationUtils {
throw new NameNotFoundException("Package " + packageName + " unavailable");
}
- public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) {
- if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
+ public static boolean isDomainVerificationIntent(Intent intent,
+ @NonNull List<ResolveInfo> candidates,
+ @PackageManager.ResolveInfoFlags int resolveInfoFlags) {
+ if (!intent.isWebIntent()) {
return false;
}
- return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0)
- || intent.hasCategory(Intent.CATEGORY_DEFAULT);
+ Set<String> categories = intent.getCategories();
+ int categoriesSize = CollectionUtils.size(categories);
+ if (categoriesSize > 2) {
+ // Specifying at least one non-app-link category
+ return false;
+ } else if (categoriesSize == 2) {
+ // Check for explicit app link intent with exactly BROWSABLE && DEFAULT
+ return intent.hasCategory(Intent.CATEGORY_DEFAULT)
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE);
+ }
+
+ // In cases where at least one browser is resolved and only one non-browser is resolved,
+ // the Intent is coerced into an app links intent, under the assumption the browser can
+ // be skipped if the app is approved at any level for the domain.
+ boolean foundBrowser = false;
+ boolean foundOneApp = false;
+
+ final int candidatesSize = candidates.size();
+ for (int index = 0; index < candidatesSize; index++) {
+ final ResolveInfo info = candidates.get(index);
+ if (info.handleAllWebDataURI) {
+ foundBrowser = true;
+ } else if (foundOneApp) {
+ // Already true, so duplicate app
+ foundOneApp = false;
+ break;
+ } else {
+ foundOneApp = true;
+ }
+ }
+
+ boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ boolean onlyOneNonBrowser = foundBrowser && foundOneApp;
+
+ // Check if matches (BROWSABLE || none) && DEFAULT
+ if (categoriesSize == 0) {
+ // No categories, run coerce case, matching DEFAULT by flags
+ return onlyOneNonBrowser && matchDefaultByFlags;
+ } else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
+ // Run coerce case, matching by explicit DEFAULT
+ return onlyOneNonBrowser;
+ } else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) {
+ // Intent matches BROWSABLE, must match DEFAULT by flags
+ return matchDefaultByFlags;
+ } else {
+ // Otherwise not matching any app link categories
+ return false;
+ }
}
static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
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/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index a80406548719..18042af139a3 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -16,6 +16,9 @@
package com.android.server.pm.verify.domain.proxy;
+import static android.os.PowerWhitelistManager.REASON_DOMAIN_VERIFICATION_V1;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -238,7 +241,8 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
mVerifierComponent.getPackageName(), allowListTimeout,
- UserHandle.USER_SYSTEM, true, "domain verification agent");
+ UserHandle.USER_SYSTEM, true, REASON_DOMAIN_VERIFICATION_V1,
+ "domain verification agent");
int size = verifications.size();
for (int index = 0; index < size; index++) {
@@ -261,7 +265,9 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppWhitelistDuration(allowListTimeout);
+ options.setTemporaryAppAllowlist(allowListTimeout,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_DOMAIN_VERIFICATION_V1, "");
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null, options.toBundle());
}
}
@@ -270,7 +276,7 @@ public class DomainVerificationProxyV1 implements DomainVerificationProxy {
private String buildHostsString(@NonNull AndroidPackage pkg) {
// The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
// not the version of the verification agent on device.
- ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
+ ArraySet<String> domains = mCollector.collectValidAutoVerifyDomains(pkg);
// v1 doesn't handle wildcard domains, so transform them here to the root
StringBuilder builder = new StringBuilder();
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
index 1ef06036021e..2ba17d3a86a5 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -16,6 +16,9 @@
package com.android.server.pm.verify.domain.proxy;
+import static android.os.PowerWhitelistManager.REASON_DOMAIN_VERIFICATION_V2;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.BroadcastOptions;
@@ -69,11 +72,14 @@ public class DomainVerificationProxyV2 implements DomainVerificationProxy {
final long allowListTimeout = mConnection.getPowerSaveTempWhitelistAppDuration();
final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppWhitelistDuration(allowListTimeout);
+ options.setTemporaryAppAllowlist(allowListTimeout,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_DOMAIN_VERIFICATION_V2, "");
mConnection.getDeviceIdleInternal().addPowerSaveTempWhitelistApp(Process.myUid(),
mVerifierComponent.getPackageName(), allowListTimeout,
- UserHandle.USER_SYSTEM, true, "domain verification agent");
+ UserHandle.USER_SYSTEM, true, REASON_DOMAIN_VERIFICATION_V2,
+ "domain verification agent");
Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION)
.setComponent(mVerifierComponent)
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index ab404dbad910..a0771c0cc8d5 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -16,18 +16,24 @@
package com.android.server.policy;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import com.android.internal.policy.IShortcutService;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -39,8 +45,9 @@ import java.io.IOException;
* Manages quick launch shortcuts by:
* <li> Keeping the local copy in sync with the database (this is an observer)
* <li> Returning a shortcut-matching intent to clients
+ * <li> Returning particular kind of application intent by special key.
*/
-class ShortcutManager {
+class ModifierShortcutManager {
private static final String TAG = "ShortcutManager";
private static final String TAG_BOOKMARKS = "bookmarks";
@@ -52,12 +59,39 @@ class ShortcutManager {
private static final String ATTRIBUTE_CATEGORY = "category";
private static final String ATTRIBUTE_SHIFT = "shift";
- private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>();
+ private final SparseArray<ShortcutInfo> mIntentShortcuts = new SparseArray<>();
private final SparseArray<ShortcutInfo> mShiftShortcuts = new SparseArray<>();
+ private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
+
+ /* Table of Application Launch keys. Maps from key codes to intent categories.
+ *
+ * These are special keys that are used to launch particular kinds of applications,
+ * such as a web browser. HID defines nearly a hundred of them in the Consumer (0x0C)
+ * usage page. We don't support quite that many yet...
+ */
+ static SparseArray<String> sApplicationLaunchKeyCategories;
+ static {
+ sApplicationLaunchKeyCategories = new SparseArray<String>();
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_EXPLORER, Intent.CATEGORY_APP_BROWSER);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CONTACTS, Intent.CATEGORY_APP_CONTACTS);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CALENDAR, Intent.CATEGORY_APP_CALENDAR);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_MUSIC, Intent.CATEGORY_APP_MUSIC);
+ sApplicationLaunchKeyCategories.append(
+ KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR);
+ }
+
private final Context mContext;
-
- public ShortcutManager(Context context) {
+ private boolean mSearchKeyShortcutPending = false;
+ private boolean mConsumeSearchKeyUp = true;
+
+ ModifierShortcutManager(Context context) {
mContext = context;
loadShortcuts();
}
@@ -70,19 +104,19 @@ class ShortcutManager {
* <p>
* This will first try an exact match (with modifiers), and then try a
* match without modifiers (primary character on a key).
- *
+ *
* @param kcm The key character map of the device on which the key was pressed.
* @param keyCode The key code.
* @param metaState The meta state, omitting any modifiers that were used
* to invoke the shortcut.
* @return The intent that matches the shortcut, or null if not found.
*/
- public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
+ private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
ShortcutInfo shortcut = null;
// If the Shift key is pressed, then search for the shift shortcuts.
boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
- SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts;
+ SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts;
// First try the exact keycode (with modifiers).
int shortcutChar = kcm.get(keyCode, metaState);
@@ -176,7 +210,7 @@ class ShortcutManager {
if (isShiftShortcut) {
mShiftShortcuts.put(shortcutChar, shortcut);
} else {
- mShortcuts.put(shortcutChar, shortcut);
+ mIntentShortcuts.put(shortcutChar, shortcut);
}
}
} catch (XmlPullParserException e) {
@@ -186,11 +220,159 @@ class ShortcutManager {
}
}
+ void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
+ throws RemoteException {
+ IShortcutService service = mShortcutKeyServices.get(shortcutCode);
+ if (service != null && service.asBinder().pingBinder()) {
+ throw new RemoteException("Key already exists.");
+ }
+
+ mShortcutKeyServices.put(shortcutCode, shortcutService);
+ }
+
+ /**
+ * Handle the shortcut to {@link IShortcutService}
+ * @param keyCode The key code of the event.
+ * @param metaState The meta key modifier state.
+ * @return True if invoked the shortcut, otherwise false.
+ */
+ private boolean handleShortcutService(int keyCode, int metaState) {
+ long shortcutCode = keyCode;
+ if ((metaState & KeyEvent.META_CTRL_ON) != 0) {
+ shortcutCode |= ((long) KeyEvent.META_CTRL_ON) << Integer.SIZE;
+ }
+
+ if ((metaState & KeyEvent.META_ALT_ON) != 0) {
+ shortcutCode |= ((long) KeyEvent.META_ALT_ON) << Integer.SIZE;
+ }
+
+ if ((metaState & KeyEvent.META_SHIFT_ON) != 0) {
+ shortcutCode |= ((long) KeyEvent.META_SHIFT_ON) << Integer.SIZE;
+ }
+
+ if ((metaState & KeyEvent.META_META_ON) != 0) {
+ shortcutCode |= ((long) KeyEvent.META_META_ON) << Integer.SIZE;
+ }
+
+ IShortcutService shortcutService = mShortcutKeyServices.get(shortcutCode);
+ if (shortcutService != null) {
+ try {
+ shortcutService.notifyShortcutKeyPressed(shortcutCode);
+ } catch (RemoteException e) {
+ mShortcutKeyServices.delete(shortcutCode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle the shortcut to {@link Intent}
+ *
+ * @param kcm the {@link KeyCharacterMap} associated with the keyboard device.
+ * @param keyCode The key code of the event.
+ * @param metaState The meta key modifier state.
+ * @return True if invoked the shortcut, otherwise false.
+ */
+ private boolean handleIntentShortcut(KeyCharacterMap kcm, int keyCode, int metaState) {
+ // Shortcuts are invoked through Search+key, so intercept those here
+ // Any printing key that is chorded with Search should be consumed
+ // even if no shortcut was invoked. This prevents text from being
+ // inadvertently inserted when using a keyboard that has built-in macro
+ // shortcut keys (that emit Search+x) and some of them are not registered.
+ if (mSearchKeyShortcutPending) {
+ if (kcm.isPrintingKey(keyCode)) {
+ mConsumeSearchKeyUp = true;
+ mSearchKeyShortcutPending = false;
+ } else {
+ return false;
+ }
+ } else if ((metaState & KeyEvent.META_META_MASK) != 0) {
+ // Invoke shortcuts using Meta.
+ metaState &= ~KeyEvent.META_META_MASK;
+ } else {
+ // Handle application launch keys.
+ String category = sApplicationLaunchKeyCategories.get(keyCode);
+ if (category != null) {
+ Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } catch (ActivityNotFoundException ex) {
+ Slog.w(TAG, "Dropping application launch key because "
+ + "the activity to which it is registered was not found: "
+ + "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
+ + " category=" + category, ex);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ final Intent shortcutIntent = getIntent(kcm, keyCode, metaState);
+ if (shortcutIntent != null) {
+ shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
+ } catch (ActivityNotFoundException ex) {
+ Slog.w(TAG, "Dropping shortcut key combination because "
+ + "the activity to which it is registered was not found: "
+ + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode), ex);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle the shortcut from {@link KeyEvent}
+ *
+ * @param event Description of the key event.
+ * @return True if invoked the shortcut, otherwise false.
+ */
+ boolean interceptKey(KeyEvent event) {
+ if (event.getRepeatCount() != 0) {
+ return false;
+ }
+
+ final int metaState = event.getModifiers();
+ final int keyCode = event.getKeyCode();
+ if (keyCode == KeyEvent.KEYCODE_SEARCH) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mSearchKeyShortcutPending = true;
+ mConsumeSearchKeyUp = false;
+ } else {
+ mSearchKeyShortcutPending = false;
+ if (mConsumeSearchKeyUp) {
+ mConsumeSearchKeyUp = false;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return false;
+ }
+
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ if (handleIntentShortcut(kcm, keyCode, metaState)) {
+ return true;
+ }
+
+ if (handleShortcutService(keyCode, metaState)) {
+ return true;
+ }
+
+ return false;
+ }
+
private static final class ShortcutInfo {
public final String title;
public final Intent intent;
- public ShortcutInfo(String title, Intent intent) {
+ ShortcutInfo(String title, Intent intent) {
this.title = title;
this.intent = intent;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b192e43c7b8..047e3b362b7a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -165,7 +165,6 @@ import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
import android.util.Log;
-import android.util.LongSparseArray;
import android.util.MutableBoolean;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -320,29 +319,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private boolean mKeyguardDrawnOnce;
- /* Table of Application Launch keys. Maps from key codes to intent categories.
- *
- * These are special keys that are used to launch particular kinds of applications,
- * such as a web browser. HID defines nearly a hundred of them in the Consumer (0x0C)
- * usage page. We don't support quite that many yet...
- */
- static SparseArray<String> sApplicationLaunchKeyCategories;
- static {
- sApplicationLaunchKeyCategories = new SparseArray<String>();
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_EXPLORER, Intent.CATEGORY_APP_BROWSER);
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL);
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_CONTACTS, Intent.CATEGORY_APP_CONTACTS);
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_CALENDAR, Intent.CATEGORY_APP_CALENDAR);
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_MUSIC, Intent.CATEGORY_APP_MUSIC);
- sApplicationLaunchKeyCategories.append(
- KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR);
- }
-
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
@@ -421,8 +397,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mSafeMode;
private WindowState mKeyguardCandidate = null;
- private LongSparseArray<IShortcutService> mShortcutKeyServices = new LongSparseArray<>();
-
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
// This is for car dock and this is updated from resource.
private boolean mEnableCarDockHomeCapture = true;
@@ -516,8 +490,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent mCarDockIntent;
Intent mDeskDockIntent;
Intent mVrHeadsetHomeIntent;
- boolean mSearchKeyShortcutPending;
- boolean mConsumeSearchKeyUp;
boolean mPendingMetaAction;
boolean mPendingCapsLockToggle;
@@ -578,7 +550,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int BRIGHTNESS_STEPS = 10;
SettingsObserver mSettingsObserver;
- ShortcutManager mShortcutManager;
+ ModifierShortcutManager mModifierShortcutManager;
PowerManager.WakeLock mBroadcastWakeLock;
PowerManager.WakeLock mPowerKeyWakeLock;
boolean mHavePendingMediaKeyRepeatWithWakeLock;
@@ -916,7 +888,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mSingleKeyGestureDetector.reset();
finishPowerKeyPress();
}
-
}
private void finishPowerKeyPress() {
@@ -1085,7 +1056,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerKeyHandled = true;
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
"Power - Long Press - Global Actions");
- showGlobalActionsInternal();
+ showGlobalActions();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
@@ -1116,14 +1087,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void powerVeryLongPress() {
switch (mVeryLongPressOnPowerBehavior) {
- case VERY_LONG_PRESS_POWER_NOTHING:
- break;
- case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
- "Power - Very Long Press - Show Global Actions");
- showGlobalActionsInternal();
- break;
+ case VERY_LONG_PRESS_POWER_NOTHING:
+ break;
+ case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
+ mPowerKeyHandled = true;
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ "Power - Very Long Press - Show Global Actions");
+ showGlobalActions();
+ break;
}
}
@@ -1646,7 +1617,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
- mShortcutManager = new ShortcutManager(context);
+ mModifierShortcutManager = new ModifierShortcutManager(context);
mUiMode = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -1917,12 +1888,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- void onLongPress(long downTime) {
- powerLongPress(downTime);
+ void onLongPress(long eventTime) {
+ powerLongPress(eventTime);
}
@Override
- void onVeryLongPress(long downTime) {
+ void onVeryLongPress(long eventTime) {
mActivityManagerInternal.prepareForPossibleShutdown();
powerVeryLongPress();
}
@@ -2525,6 +2496,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPendingCapsLockToggle = false;
}
+ if (isUserSetupComplete() && !keyguardOn) {
+ if (mModifierShortcutManager.interceptKey(event)) {
+ dismissKeyboardShortcutsMenu();
+ mPendingMetaAction = false;
+ mPendingCapsLockToggle = false;
+ return key_consumed;
+ }
+ }
+
switch(keyCode) {
case KeyEvent.KEYCODE_HOME:
// First we always handle the home key here, so applications
@@ -2550,20 +2530,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
break;
- case KeyEvent.KEYCODE_SEARCH:
- if (down) {
- if (repeatCount == 0) {
- mSearchKeyShortcutPending = true;
- mConsumeSearchKeyUp = false;
- }
- } else {
- mSearchKeyShortcutPending = false;
- if (mConsumeSearchKeyUp) {
- mConsumeSearchKeyUp = false;
- return key_consumed;
- }
- }
- return 0;
case KeyEvent.KEYCODE_APP_SWITCH:
if (!keyguardOn) {
if (down && repeatCount == 0) {
@@ -2771,114 +2737,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
- // Shortcuts are invoked through Search+key, so intercept those here
- // Any printing key that is chorded with Search should be consumed
- // even if no shortcut was invoked. This prevents text from being
- // inadvertently inserted when using a keyboard that has built-in macro
- // shortcut keys (that emit Search+x) and some of them are not registered.
- if (mSearchKeyShortcutPending) {
- final KeyCharacterMap kcm = event.getKeyCharacterMap();
- if (kcm.isPrintingKey(keyCode)) {
- mConsumeSearchKeyUp = true;
- mSearchKeyShortcutPending = false;
- if (down && repeatCount == 0 && !keyguardOn) {
- Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState);
- if (shortcutIntent != null) {
- shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
- dismissKeyboardShortcutsMenu();
- } catch (ActivityNotFoundException ex) {
- Slog.w(TAG, "Dropping shortcut key combination because "
- + "the activity to which it is registered was not found: "
- + "SEARCH+" + KeyEvent.keyCodeToString(keyCode), ex);
- }
- } else {
- Slog.i(TAG, "Dropping unregistered shortcut key combination: "
- + "SEARCH+" + KeyEvent.keyCodeToString(keyCode));
- }
- }
- return key_consumed;
- }
- }
-
- // Invoke shortcuts using Meta.
- if (down && repeatCount == 0 && !keyguardOn
- && (metaState & KeyEvent.META_META_ON) != 0) {
- final KeyCharacterMap kcm = event.getKeyCharacterMap();
- if (kcm.isPrintingKey(keyCode)) {
- Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
- metaState & ~(KeyEvent.META_META_ON
- | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
- if (shortcutIntent != null) {
- shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivityAsUser(shortcutIntent, UserHandle.CURRENT);
- dismissKeyboardShortcutsMenu();
- } catch (ActivityNotFoundException ex) {
- Slog.w(TAG, "Dropping shortcut key combination because "
- + "the activity to which it is registered was not found: "
- + "META+" + KeyEvent.keyCodeToString(keyCode), ex);
- }
- return key_consumed;
- }
- }
- }
-
- // Handle application launch keys.
- if (down && repeatCount == 0 && !keyguardOn) {
- String category = sApplicationLaunchKeyCategories.get(keyCode);
- if (category != null) {
- Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivityAsUser(intent, UserHandle.CURRENT);
- dismissKeyboardShortcutsMenu();
- } catch (ActivityNotFoundException ex) {
- Slog.w(TAG, "Dropping application launch key because "
- + "the activity to which it is registered was not found: "
- + "keyCode=" + keyCode + ", category=" + category, ex);
- }
- return key_consumed;
- }
- }
-
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
return key_consumed;
}
- if (down) {
- long shortcutCode = keyCode;
- if (event.isCtrlPressed()) {
- shortcutCode |= ((long) KeyEvent.META_CTRL_ON) << Integer.SIZE;
- }
-
- if (event.isAltPressed()) {
- shortcutCode |= ((long) KeyEvent.META_ALT_ON) << Integer.SIZE;
- }
-
- if (event.isShiftPressed()) {
- shortcutCode |= ((long) KeyEvent.META_SHIFT_ON) << Integer.SIZE;
- }
-
- if (event.isMetaPressed()) {
- shortcutCode |= ((long) KeyEvent.META_META_ON) << Integer.SIZE;
- }
-
- IShortcutService shortcutService = mShortcutKeyServices.get(shortcutCode);
- if (shortcutService != null) {
- try {
- if (isUserSetupComplete()) {
- shortcutService.notifyShortcutKeyPressed(shortcutCode);
- }
- } catch (RemoteException e) {
- mShortcutKeyServices.delete(shortcutCode);
- }
- return key_consumed;
- }
- }
-
// Reserve all the META modifier combos for system behavior
if ((metaState & KeyEvent.META_META_ON) != 0) {
return key_consumed;
@@ -3064,12 +2927,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
synchronized (mLock) {
- IShortcutService service = mShortcutKeyServices.get(shortcutCode);
- if (service != null && service.asBinder().pingBinder()) {
- throw new RemoteException("Key already exists.");
- }
-
- mShortcutKeyServices.put(shortcutCode, shortcutService);
+ mModifierShortcutManager.registerShortcutKey(shortcutCode, shortcutService);
}
}
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 3dafb0ce21ef..cae209353361 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -78,8 +78,8 @@ public final class SingleKeyGestureDetector {
* new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) {
* int getMaxMultiPressCount() { // maximum multi press count. }
* void onPress(long downTime) { // short press behavior. }
- * void onLongPress() { // long press behavior. }
- * void onVeryLongPress() { // very long press behavior. }
+ * void onLongPress(long eventTime) { // long press behavior. }
+ * void onVeryLongPress(long eventTime) { // very long press behavior. }
* void onMultiPress(long downTime, int count) { // multi press behavior. }
* };
* </pre>
@@ -135,11 +135,11 @@ public final class SingleKeyGestureDetector {
/**
* Callback when long press has been detected.
*/
- void onLongPress(long downTime) {}
+ void onLongPress(long eventTime) {}
/**
* Callback when very long press has been detected.
*/
- void onVeryLongPress(long downTime) {}
+ void onVeryLongPress(long eventTime) {}
@Override
public String toString() {
@@ -174,9 +174,9 @@ public final class SingleKeyGestureDetector {
// same key down.
if (mDownKeyCode == keyCode) {
if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0
- && !mHandledByLongPress) {
+ && mActiveRule.supportLongPress() && !mHandledByLongPress) {
if (DEBUG) {
- Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode));
+ Log.i(TAG, "Long press key " + KeyEvent.keyCodeToString(keyCode));
}
mHandledByLongPress = true;
mHandler.removeMessages(MSG_KEY_LONG_PRESS);
@@ -192,7 +192,6 @@ public final class SingleKeyGestureDetector {
if (DEBUG) {
Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode));
}
-
reset();
}
mDownKeyCode = keyCode;
@@ -203,6 +202,9 @@ public final class SingleKeyGestureDetector {
for (int index = 0; index < count; index++) {
final SingleKeyRule rule = mRules.get(index);
if (rule.shouldInterceptKey(keyCode)) {
+ if (DEBUG) {
+ Log.i(TAG, "Intercept key by rule " + rule);
+ }
mActiveRule = rule;
break;
}
@@ -254,6 +256,7 @@ public final class SingleKeyGestureDetector {
if (mHandledByLongPress) {
mHandledByLongPress = false;
+ mKeyPressCounter = 0;
return true;
}
@@ -261,6 +264,9 @@ public final class SingleKeyGestureDetector {
if (event.getKeyCode() == mActiveRule.mKeyCode) {
// Directly trigger short press when max count is 1.
if (mActiveRule.getMaxMultiPressCount() == 1) {
+ if (DEBUG) {
+ Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
+ }
mActiveRule.onPress(downTime);
return true;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index bc117094dd68..29adde37ab3b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -5073,7 +5073,7 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
- public void userActivity(long eventTime, int event, int flags) {
+ public void userActivity(int displayId, long eventTime, int event, int flags) {
final long now = mClock.uptimeMillis();
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED
diff --git a/services/core/java/com/android/server/role/OWNERS b/services/core/java/com/android/server/role/OWNERS
index 31e3549d9111..dafdf0f8075c 100644
--- a/services/core/java/com/android/server/role/OWNERS
+++ b/services/core/java/com/android/server/role/OWNERS
@@ -1,5 +1 @@
-svetoslavganov@google.com
-zhanghai@google.com
-evanseverson@google.com
-eugenesusla@google.com
-ntmyren@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/services/core/java/com/android/server/servicewatcher/OWNERS b/services/core/java/com/android/server/servicewatcher/OWNERS
new file mode 100644
index 000000000000..ced619f05f1d
--- /dev/null
+++ b/services/core/java/com/android/server/servicewatcher/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 25692
+
+sooniln@google.com
+wyattriley@google.com
+
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/servicewatcher/ServiceWatcher.java
index 8a2894c84cc4..5d49663209b7 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/servicewatcher/ServiceWatcher.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.servicewatcher;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_NOT_FOREGROUND;
@@ -52,6 +52,7 @@ import com.android.internal.annotations.Immutable;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.FgThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
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/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
index ebe9733e5d55..212f81f72b24 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
@@ -35,8 +35,7 @@ import java.util.TimerTask;
* HAL whenever they expire.
*/
public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
- // TODO(b/166328980): Reduce this to 1000 as soon as HAL is fixed.
- private static final long TIMEOUT_MS = 10000;
+ private static final long TIMEOUT_MS = 3000;
private static final String TAG = "SoundTriggerHw2Watchdog";
private final @NonNull
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..a0e2286f72e2 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -42,8 +42,8 @@ import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageVolume;
import android.service.storage.ExternalStorageService;
import android.service.storage.IExternalStorageService;
-import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -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,15 +74,17 @@ 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<>();
+ @GuardedBy("mSessionsLock") private final Map<String, Session> mSessions = new HashMap<>();
+ @GuardedBy("mSessionsLock") private final SparseArray<Integer> mUidsBlockedOnIo = new SparseArray<>();
private final HandlerThread mHandlerThread;
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
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);
}
/**
@@ -242,7 +249,8 @@ public final class StorageUserConnection {
public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
@StorageManager.AppIoBlockedReason int reason) {
synchronized (mSessionsLock) {
- mUidsBlockedOnIo.add(uid);
+ int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0);
+ mUidsBlockedOnIo.put(uid, ++ioBlockedCounter);
}
}
@@ -255,7 +263,12 @@ public final class StorageUserConnection {
public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
@StorageManager.AppIoBlockedReason int reason) {
synchronized (mSessionsLock) {
- mUidsBlockedOnIo.remove(uid);
+ int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0);
+ if (ioBlockedCounter == 0) {
+ mUidsBlockedOnIo.remove(uid);
+ } else {
+ mUidsBlockedOnIo.put(uid, --ioBlockedCounter);
+ }
}
}
@@ -317,6 +330,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 +431,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/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 531c62c5b4e9..0b51488280e0 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -39,8 +39,8 @@ import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.ServiceWatcher;
-import com.android.server.ServiceWatcher.BoundService;
+import com.android.server.servicewatcher.ServiceWatcher;
+import com.android.server.servicewatcher.ServiceWatcher.BoundService;
import java.util.Objects;
import java.util.function.Predicate;
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
index 5772dea287fc..e54b40eb334c 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server.uri;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
@@ -58,6 +60,19 @@ public interface UriGrantsManagerInternal {
void grantUriPermissionUncheckedFromIntent(
NeededUriGrants needed, UriPermissionOwner owner);
+ /**
+ * Creates a new stateful object to track uri permission grants. This is needed to maintain
+ * state when managing grants via {@link UriGrantsManagerService#grantUriPermissionFromOwner},
+ * {@link #revokeUriPermissionFromOwner}, etc.
+ *
+ * @param name A name for the object. This is only used for logcat/dumpsys logging, so there
+ * are no uniqueness or other requirements, but it is recommended to make the
+ * name sufficiently readable so that the relevant code area can be determined
+ * easily when this name shows up in a bug report.
+ * @return An opaque owner token for tracking uri permission grants.
+ * @see UriPermissionOwner
+ * @see UriGrantsManagerService
+ */
IBinder newUriPermissionOwner(String name);
/**
@@ -74,33 +89,39 @@ public interface UriGrantsManagerInternal {
*/
void removeUriPermissionsForPackage(
String packageName, int userHandle, boolean persistable, boolean targetOnly);
+
/**
- * Remove any {@link UriPermission} associated with the owner whose values match the given
- * filtering parameters.
- *
- * @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
- * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
- * @param mode The modes (as a bitmask) to revoke.
- * @param userId The userId in which the uri is to be resolved.
+ * Like {@link #revokeUriPermissionFromOwner(IBinder, Uri, int, int, String, int)} but applies
+ * to all target packages and all target users.
*/
- void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId);
+ void revokeUriPermissionFromOwner(@NonNull IBinder token, @Nullable Uri uri, int mode,
+ int userId);
/**
* Remove any {@link UriPermission} associated with the owner whose values match the given
* filtering parameters.
*
* @param token An opaque owner token as returned by {@link #newUriPermissionOwner(String)}.
- * @param uri This uri must NOT contain an embedded userId. {@code null} to apply to all Uris.
- * @param mode The modes (as a bitmask) to revoke.
- * @param userId The userId in which the uri is to be resolved.
- * @param targetPkg Calling package name to match, or {@code null} to apply to all packages.
- * @param targetUserId Calling user to match, or {@link UserHandle#USER_ALL} to apply to all
- * users.
+ * @param uri The content uri for which the permission grant should be revoked. This uri
+ * must NOT contain an embedded userId; use
+ * {@link android.content.ContentProvider#getUriWithoutUserId(Uri)} if needed.
+ * This param may be {@code null} to revoke grants for all uris tracked by the
+ * provided owner token.
+ * @param mode The modes (as a bitmask) to revoke. See
+ * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+ * @param userId The userId in which the given uri is to be resolved. If the {@code uri}
+ * param is {@code null}, this param is ignored since permissions for all
+ * uris will be revoked.
+ * @param targetPkg Target package name to match (app that received the grant), or
+ * {@code null} to apply to all packages.
+ * @param targetUserId Target user to match (userId of the app that received the grant), or
+ * {@link UserHandle#USER_ALL} to apply to all users.
*/
- void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId,
- String targetPkg, int targetUserId);
+ void revokeUriPermissionFromOwner(@NonNull IBinder token, @Nullable Uri uri, int mode,
+ int userId, @Nullable String targetPkg, int targetUserId);
boolean checkAuthorityGrants(
int callingUid, ProviderInfo cpi, int userId, boolean checkUser);
+
void dump(PrintWriter pw, boolean dumpAll, String dumpPackage);
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index dcc15999d882..44545ed4898a 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -33,17 +33,13 @@ import static android.os.Process.ROOT_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myUid;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.server.uri.UriGrantsManagerService.H.PERSIST_URI_GRANTS_MSG;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -82,7 +78,6 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -94,9 +89,7 @@ import com.google.android.collect.Maps;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -104,7 +97,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -211,6 +203,21 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
+ /**
+ * Grant uri permissions to the specified app.
+ *
+ * @param token An opaque owner token for tracking the permissions. See
+ * {@link UriGrantsManagerInternal#newUriPermissionOwner}.
+ * @param fromUid The uid of the grantor app that has permissions to the uri. Permissions
+ * will be granted on behalf of this app.
+ * @param targetPkg The package name of the grantor app that has permissions to the uri.
+ * Permissions will be granted on behalf of this app.
+ * @param uri The uri for which permissions should be granted. This uri must NOT contain an
+ * embedded userId; use {@link ContentProvider#getUriWithoutUserId(Uri)} if needed.
+ * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc.
+ * @param sourceUserId The userId in which the uri is to be resolved.
+ * @param targetUserId The userId of the target app to receive the grant.
+ */
@Override
public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg,
Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
@@ -219,12 +226,11 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
/**
- * @param uri This uri must NOT contain an embedded userId.
- * @param sourceUserId The userId in which the uri is to be resolved.
- * @param targetUserId The userId of the app that receives the grant.
+ * See {@link #grantUriPermissionFromOwner(IBinder, int, String, Uri, int, int, int)}.
*/
- private void grantUriPermissionFromOwnerUnlocked(IBinder token, int fromUid, String targetPkg,
- Uri uri, final int modeFlags, int sourceUserId, int targetUserId) {
+ private void grantUriPermissionFromOwnerUnlocked(@NonNull IBinder token, int fromUid,
+ @NonNull String targetPkg, @NonNull Uri uri, final int modeFlags,
+ int sourceUserId, int targetUserId) {
targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
"grantUriPermissionFromOwner", null);
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index b6ddd93af3b8..b2db9f5af07e 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -65,7 +65,7 @@ public class UnderlyingNetworkTracker {
@NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ private boolean mIsQuitting = false;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -151,7 +151,7 @@ public class UnderlyingNetworkTracker {
mVcnContext.ensureRunningOnLooperThread();
// Don't bother re-filing NetworkRequests if this Tracker has been torn down.
- if (!mIsRunning) {
+ if (mIsQuitting) {
return;
}
@@ -205,7 +205,7 @@ public class UnderlyingNetworkTracker {
}
mCellBringupCallbacks.clear();
- mIsRunning = false;
+ mIsQuitting = true;
}
/** Returns whether the currently selected Network matches the given network. */
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 6ad30b544257..9d39c67d27fb 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+
import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
@@ -84,6 +86,13 @@ public class Vcn extends Handler {
*/
private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+ /**
+ * A GatewayConnection owned by this VCN quit.
+ *
+ * @param obj VcnGatewayConnectionConfig
+ */
+ private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@@ -208,6 +217,9 @@ public class Vcn extends Handler {
case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
break;
+ case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
+ handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
@@ -263,7 +275,7 @@ public class Vcn extends Handler {
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
- if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
if (VDBG) {
Slog.v(
getLogTag(),
@@ -278,7 +290,7 @@ public class Vcn extends Handler {
// up
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
- if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
+ if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(
getLogTag(),
"Bringing up new VcnGatewayConnection for request " + request.requestId);
@@ -289,12 +301,21 @@ public class Vcn extends Handler {
mSubscriptionGroup,
mLastSnapshot,
gatewayConnectionConfig,
- new VcnGatewayStatusCallbackImpl());
+ new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
+ private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
+ Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
+ mVcnGatewayConnections.remove(config);
+
+ // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
+ // start a new GatewayConnection)
+ mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
+ }
+
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
@@ -305,9 +326,10 @@ public class Vcn extends Handler {
}
}
- private boolean requestSatisfiedByGatewayConnectionConfig(
+ private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
+ builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
for (int cap : config.getAllExposedCapabilities()) {
builder.addCapability(cap);
}
@@ -339,9 +361,23 @@ public class Vcn extends Handler {
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
+
+ /** Called by a VcnGatewayConnection to indicate that it has fully torn down. */
+ void onQuit();
}
private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ public final VcnGatewayConnectionConfig mGatewayConnectionConfig;
+
+ VcnGatewayStatusCallbackImpl(VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ mGatewayConnectionConfig = gatewayConnectionConfig;
+ }
+
+ @Override
+ public void onQuit() {
+ sendMessage(obtainMessage(MSG_EVENT_GATEWAY_CONNECTION_QUIT, mGatewayConnectionConfig));
+ }
+
@Override
public void onEnteredSafeMode() {
sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 06748a3aa2d1..6bc9978a0731 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -168,6 +168,8 @@ public class VcnGatewayConnection extends StateMachine {
private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: ";
private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST =
"Underlying Network lost";
+ private static final String DISCONNECT_REASON_NETWORK_AGENT_UNWANTED =
+ "NetworkAgent was unwanted";
private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
private static final int TOKEN_ALL = Integer.MIN_VALUE;
@@ -379,13 +381,16 @@ public class VcnGatewayConnection extends StateMachine {
/** The reason why the disconnect was requested. */
@NonNull public final String reason;
- EventDisconnectRequestedInfo(@NonNull String reason) {
+ public final boolean shouldQuit;
+
+ EventDisconnectRequestedInfo(@NonNull String reason, boolean shouldQuit) {
this.reason = Objects.requireNonNull(reason);
+ this.shouldQuit = shouldQuit;
}
@Override
public int hashCode() {
- return Objects.hash(reason);
+ return Objects.hash(reason, shouldQuit);
}
@Override
@@ -395,7 +400,7 @@ public class VcnGatewayConnection extends StateMachine {
}
final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other;
- return reason.equals(rhs.reason);
+ return reason.equals(rhs.reason) && shouldQuit == rhs.shouldQuit;
}
}
@@ -488,8 +493,14 @@ public class VcnGatewayConnection extends StateMachine {
*/
@NonNull private final VcnWakeLock mWakeLock;
- /** Running state of this VcnGatewayConnection. */
- private boolean mIsRunning = true;
+ /**
+ * Whether the VcnGatewayConnection is in the process of irreversibly quitting.
+ *
+ * <p>This variable is false for the lifecycle of the VcnGatewayConnection, until a command to
+ * teardown has been received. This may be flipped due to events such as the Network becoming
+ * unwanted, the owning VCN entering safe mode, or an irrecoverable internal failure.
+ */
+ private boolean mIsQuitting = false;
/**
* The token used by the primary/current/active session.
@@ -622,10 +633,8 @@ public class VcnGatewayConnection extends StateMachine {
* <p>Once torn down, this VcnTunnel CANNOT be started again.
*/
public void teardownAsynchronously() {
- sendMessageAndAcquireWakeLock(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ALL,
- new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
+ sendDisconnectRequestedAndAcquireWakelock(
+ DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */);
// TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
// is also called asynchronously when a NetworkAgent becomes unwanted
@@ -646,6 +655,8 @@ public class VcnGatewayConnection extends StateMachine {
cancelSafeModeAlarm();
mUnderlyingNetworkTracker.teardown();
+
+ mGatewayStatusCallback.onQuit();
}
/**
@@ -693,7 +704,7 @@ public class VcnGatewayConnection extends StateMachine {
private void acquireWakeLock() {
mVcnContext.ensureRunningOnLooperThread();
- if (mIsRunning) {
+ if (!mIsQuitting) {
mWakeLock.acquire();
}
}
@@ -892,7 +903,7 @@ public class VcnGatewayConnection extends StateMachine {
TOKEN_ALL,
0 /* arg2 */,
new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */));
mDisconnectRequestAlarm =
createScheduledAlarm(
DISCONNECT_REQUEST_ALARM,
@@ -909,7 +920,8 @@ public class VcnGatewayConnection extends StateMachine {
// Cancel any existing disconnect due to previous loss of underlying network
removeEqualMessages(
EVENT_DISCONNECT_REQUESTED,
- new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */));
}
private void setRetryTimeoutAlarm(long delay) {
@@ -1041,11 +1053,8 @@ public class VcnGatewayConnection extends StateMachine {
enterState();
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessageAndAcquireWakeLock(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ALL,
- new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ sendDisconnectRequestedAndAcquireWakelock(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
}
}
@@ -1083,11 +1092,8 @@ public class VcnGatewayConnection extends StateMachine {
processStateMsg(msg);
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessageAndAcquireWakeLock(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ALL,
- new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ sendDisconnectRequestedAndAcquireWakelock(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
}
// Attempt to release the WakeLock - only possible if the Handler queue is empty
@@ -1104,11 +1110,8 @@ public class VcnGatewayConnection extends StateMachine {
exitState();
} catch (Exception e) {
Slog.wtf(TAG, "Uncaught exception", e);
- sendMessageAndAcquireWakeLock(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ALL,
- new EventDisconnectRequestedInfo(
- DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ sendDisconnectRequestedAndAcquireWakelock(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */);
}
}
@@ -1141,11 +1144,11 @@ public class VcnGatewayConnection extends StateMachine {
}
}
- protected void handleDisconnectRequested(String msg) {
+ protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) {
// TODO(b/180526152): notify VcnStatusCallback for Network loss
- Slog.v(TAG, "Tearing down. Cause: " + msg);
- mIsRunning = false;
+ Slog.v(TAG, "Tearing down. Cause: " + info.reason);
+ mIsQuitting = info.shouldQuit;
teardownNetwork();
@@ -1177,7 +1180,7 @@ public class VcnGatewayConnection extends StateMachine {
private class DisconnectedState extends BaseState {
@Override
protected void enterState() {
- if (!mIsRunning) {
+ if (mIsQuitting) {
quitNow(); // Ignore all queued events; cleanup is complete.
}
@@ -1200,9 +1203,11 @@ public class VcnGatewayConnection extends StateMachine {
}
break;
case EVENT_DISCONNECT_REQUESTED:
- mIsRunning = false;
+ if (((EventDisconnectRequestedInfo) msg.obj).shouldQuit) {
+ mIsQuitting = true;
- quitNow();
+ quitNow();
+ }
break;
default:
logUnhandledMessage(msg);
@@ -1284,10 +1289,11 @@ public class VcnGatewayConnection extends StateMachine {
break;
case EVENT_DISCONNECT_REQUESTED:
+ EventDisconnectRequestedInfo info = ((EventDisconnectRequestedInfo) msg.obj);
+ mIsQuitting = info.shouldQuit;
teardownNetwork();
- String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
- if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ if (info.reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
// TODO(b/180526152): notify VcnStatusCallback for Network loss
// Will trigger EVENT_SESSION_CLOSED immediately.
@@ -1300,7 +1306,7 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_SESSION_CLOSED:
mIkeSession = null;
- if (mIsRunning && mUnderlying != null) {
+ if (!mIsQuitting && mUnderlying != null) {
transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
} else {
teardownNetwork();
@@ -1391,7 +1397,7 @@ public class VcnGatewayConnection extends StateMachine {
transitionTo(mConnectedState);
break;
case EVENT_DISCONNECT_REQUESTED:
- handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafeMode();
@@ -1438,6 +1444,7 @@ public class VcnGatewayConnection extends StateMachine {
mVcnContext.getVcnNetworkProvider()) {
@Override
public void unwanted() {
+ Slog.d(TAG, "NetworkAgent was unwanted");
teardownAsynchronously();
}
@@ -1471,7 +1478,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull IpSecTransform transform,
int direction) {
try {
- // TODO(b/180163196): Set underlying network of tunnel interface
+ tunnelIface.setUnderlyingNetwork(underlyingNetwork);
// Transforms do not need to be persisted; the IkeSession will keep them alive
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
@@ -1577,7 +1584,7 @@ public class VcnGatewayConnection extends StateMachine {
setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
break;
case EVENT_DISCONNECT_REQUESTED:
- handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafeMode();
@@ -1682,7 +1689,7 @@ public class VcnGatewayConnection extends StateMachine {
transitionTo(mConnectingState);
break;
case EVENT_DISCONNECT_REQUESTED:
- handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
mGatewayStatusCallback.onEnteredSafeMode();
@@ -1905,13 +1912,13 @@ public class VcnGatewayConnection extends StateMachine {
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- boolean isRunning() {
- return mIsRunning;
+ boolean isQuitting() {
+ return mIsQuitting;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- void setIsRunning(boolean isRunning) {
- mIsRunning = isRunning;
+ void setIsQuitting(boolean isQuitting) {
+ mIsQuitting = isQuitting;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1924,6 +1931,14 @@ public class VcnGatewayConnection extends StateMachine {
mIkeSession = session;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(reason, shouldQuit));
+ }
+
private IkeSessionParams buildIkeParams() {
// TODO: Implement this once IkeSessionParams is persisted
return null;
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index bfeec011a2c9..a90969599159 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -67,9 +67,7 @@ public class VcnNetworkProvider extends NetworkProvider {
mListeners.add(listener);
// Send listener all cached requests
- for (NetworkRequestEntry entry : mRequests.values()) {
- notifyListenerForEvent(listener, entry);
- }
+ resendAllRequests(listener);
}
/** Unregisters the specified listener from receiving future NetworkRequests. */
@@ -78,6 +76,14 @@ public class VcnNetworkProvider extends NetworkProvider {
mListeners.remove(listener);
}
+ /** Sends all cached NetworkRequest(s) to the specified listener. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void resendAllRequests(@NonNull NetworkRequestListener listener) {
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ notifyListenerForEvent(listener, entry);
+ }
+ }
+
private void notifyListenerForEvent(
@NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) {
listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId);
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index 685dce4683d7..96f84dc65e1d 100644
--- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
+++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.CombinedVibrationEffect;
@@ -33,7 +34,11 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener {
private final Object mLock = new Object();
private final Handler mHandler;
- private final InputManager mInputManager;
+ private final Context mContext;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private InputManager mInputManager;
@GuardedBy("mLock")
private final SparseArray<VibratorManager> mInputDeviceVibrators = new SparseArray<>();
@@ -47,7 +52,13 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener {
InputDeviceDelegate(Context context, Handler handler) {
mHandler = handler;
- mInputManager = context.getSystemService(InputManager.class);
+ mContext = context;
+ }
+
+ public void onSystemReady() {
+ synchronized (mLock) {
+ mInputManager = mContext.getSystemService(InputManager.class);
+ }
}
@Override
@@ -116,6 +127,10 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener {
*/
public boolean updateInputDeviceVibrators(boolean vibrateInputDevices) {
synchronized (mLock) {
+ if (mInputManager == null) {
+ // Ignore update, service not loaded yet so change cannot be applied.
+ return false;
+ }
if (vibrateInputDevices == mShouldVibrateInputDevices) {
// No need to update if settings haven't changed.
return false;
@@ -150,6 +165,10 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener {
private void updateInputDevice(int deviceId) {
synchronized (mLock) {
+ if (mInputManager == null) {
+ // Ignore update, service not loaded yet so change cannot be applied.
+ return;
+ }
if (!mShouldVibrateInputDevices) {
// No need to keep this device vibrator if setting is off.
return;
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/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 334129d6bde9..4a07c1ac1e39 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IUidObserver;
import android.content.Context;
@@ -57,8 +58,6 @@ final class VibrationSettings {
private final Object mLock = new Object();
private final Context mContext;
- private final Vibrator mVibrator;
- private final AudioManager mAudioManager;
private final SettingsObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
@@ -68,6 +67,13 @@ final class VibrationSettings {
private final SparseArray<VibrationEffect> mFallbackEffects;
@GuardedBy("mLock")
+ @Nullable
+ private Vibrator mVibrator;
+ @GuardedBy("mLock")
+ @Nullable
+ private AudioManager mAudioManager;
+
+ @GuardedBy("mLock")
private boolean mVibrateInputDevices;
@GuardedBy("mLock")
private boolean mVibrateWhenRinging;
@@ -86,22 +92,9 @@ final class VibrationSettings {
VibrationSettings(Context context, Handler handler) {
mContext = context;
- mVibrator = context.getSystemService(Vibrator.class);
- mAudioManager = context.getSystemService(AudioManager.class);
mSettingObserver = new SettingsObserver(handler);
mUidObserver = new UidObserver();
- registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
- registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
- registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
- registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
- registerSettingsObserver(
- Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
- registerSettingsObserver(
- Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
- registerSettingsObserver(
- Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
-
VibrationEffect clickEffect = createEffectFromResource(
com.android.internal.R.array.config_virtualKeyVibePattern);
VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
@@ -119,6 +112,15 @@ final class VibrationSettings {
mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK,
VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));
+ // Update with current values from settings.
+ updateSettings();
+ }
+
+ public void onSystemReady() {
+ synchronized (mLock) {
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ mAudioManager = mContext.getSystemService(AudioManager.class);
+ }
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
@@ -148,7 +150,18 @@ final class VibrationSettings {
}
});
- // Update with current values from settings.
+ registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
+ registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
+ registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
+ registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
+ registerSettingsObserver(
+ Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
+ registerSettingsObserver(
+ Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
+ registerSettingsObserver(
+ Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
+
+ // Update with newly loaded services.
updateSettings();
}
@@ -178,17 +191,21 @@ final class VibrationSettings {
* @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
*/
public int getDefaultIntensity(int usageHint) {
- if (isRingtone(usageHint)) {
- return mVibrator.getDefaultRingVibrationIntensity();
- } else if (isNotification(usageHint)) {
- return mVibrator.getDefaultNotificationVibrationIntensity();
- } else if (isHapticFeedback(usageHint)) {
- return mVibrator.getDefaultHapticFeedbackIntensity();
- } else if (isAlarm(usageHint)) {
+ if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
- } else {
- return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
+ synchronized (mLock) {
+ if (mVibrator != null) {
+ if (isRingtone(usageHint)) {
+ return mVibrator.getDefaultRingVibrationIntensity();
+ } else if (isNotification(usageHint)) {
+ return mVibrator.getDefaultNotificationVibrationIntensity();
+ } else if (isHapticFeedback(usageHint)) {
+ return mVibrator.getDefaultHapticFeedbackIntensity();
+ }
+ }
+ }
+ return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
/**
@@ -234,8 +251,11 @@ final class VibrationSettings {
if (!isRingtone(usageHint)) {
return true;
}
- int ringerMode = mAudioManager.getRingerModeInternal();
synchronized (mLock) {
+ if (mAudioManager == null) {
+ return false;
+ }
+ int ringerMode = mAudioManager.getRingerModeInternal();
if (mVibrateWhenRinging) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else if (mApplyRampingRinger) {
@@ -304,12 +324,12 @@ final class VibrationSettings {
mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0;
mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- mVibrator.getDefaultHapticFeedbackIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
mNotificationIntensity = getSystemSetting(
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- mVibrator.getDefaultNotificationVibrationIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
- mVibrator.getDefaultRingVibrationIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
@@ -346,15 +366,15 @@ final class VibrationSettings {
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
- mVibrator.getDefaultHapticFeedbackIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
mNotificationIntensity);
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
- mVibrator.getDefaultNotificationVibrationIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
mRingIntensity);
proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
- mVibrator.getDefaultRingVibrationIntensity());
+ getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 95f6059c482e..eaba083c551c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -87,8 +87,8 @@ final class VibratorController {
static native long vibratorPerformEffect(
long nativePtr, long effect, long strength, long vibrationId);
- static native void vibratorPerformComposedEffect(long nativePtr,
- VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
+ static native long vibratorPerformComposedEffect(
+ long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
static native void vibratorSetExternalControl(long nativePtr, boolean enabled);
@@ -269,13 +269,9 @@ final class VibratorController {
VibrationEffect.Composition.PrimitiveEffect[] primitives =
effect.getPrimitiveEffects().toArray(
new VibrationEffect.Composition.PrimitiveEffect[0]);
- mNativeWrapper.compose(primitives, vibrationId);
- notifyVibratorOnLocked();
- // Compose don't actually give us an estimated duration, so we just guess here.
- long duration = 0;
- for (VibrationEffect.Composition.PrimitiveEffect primitive : primitives) {
- // TODO(b/177807015): use exposed durations from IVibrator here instead
- duration += 20 + primitive.delay;
+ long duration = mNativeWrapper.compose(primitives, vibrationId);
+ if (duration > 0) {
+ notifyVibratorOnLocked();
}
return duration;
}
@@ -393,9 +389,9 @@ final class VibratorController {
}
/** Turns vibrator on to perform one of the supported composed effects. */
- public void compose(
+ public long compose(
VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
- VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
+ return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect,
vibrationId);
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 175085475b6c..c9751bb7abe4 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -127,9 +127,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@GuardedBy("mLock")
private ExternalVibrationHolder mCurrentExternalVibration;
- private VibrationSettings mVibrationSettings;
- private VibrationScaler mVibrationScaler;
- private InputDeviceDelegate mInputDeviceDelegate;
+ private final VibrationSettings mVibrationSettings;
+ private final VibrationScaler mVibrationScaler;
+ private final InputDeviceDelegate mInputDeviceDelegate;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -170,6 +170,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mContext = context;
mHandler = injector.createHandler(Looper.myLooper());
+ mVibrationSettings = new VibrationSettings(mContext, mHandler);
+ mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
+ mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
+
VibrationCompleteListener listener = new VibrationCompleteListener(this);
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init(listener);
@@ -224,12 +228,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.v(TAG, "Initializing VibratorManager service...");
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
try {
- mVibrationSettings = new VibrationSettings(mContext, mHandler);
- mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
- mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);
+ mVibrationSettings.onSystemReady();
+ mInputDeviceDelegate.onSystemReady();
mVibrationSettings.addListener(this::updateServiceState);
+ // Will update settings and input devices.
updateServiceState();
} finally {
Slog.v(TAG, "VibratorManager service initialized");
@@ -340,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);
@@ -1134,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));
@@ -1142,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 c1fe4887da57..298128a6a222 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -132,7 +132,6 @@ import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.wm.ActivityRecordProto.FROZEN_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -212,9 +211,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
+import static com.android.server.wm.WindowManagerService.MIN_FIXED_ORIENTATION_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;
@@ -344,7 +344,6 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -582,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;
@@ -732,9 +728,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// windows, where the app hasn't had time to set a value on the window.
int mRotationAnimationHint = -1;
- ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
- ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
-
private AppSaturationInfo mLastAppSaturationInfo;
private final ColorDisplayService.ColorTransformController mColorTransformController =
@@ -763,6 +756,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
+ // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be used
+ // without security checks
+ final Binder shareableActivityToken = new Binder();
+
// Tracking cookie for the launch of this activity and it's task.
IBinder mLaunchCookie;
@@ -911,7 +908,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(Integer.toHexString(taskDescription.getStatusBarColor()));
pw.print(" navigationBarColor=");
pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
- pw.print(" backgroundColorFloating=");
+ pw.print(prefix); pw.print(" backgroundColorFloating=");
pw.println(Integer.toHexString(
taskDescription.getBackgroundColorFloating()));
}
@@ -999,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) {
@@ -1031,10 +1028,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(" mVisibleSetFromTransferredStartingWindow="
+ mVisibleSetFromTransferredStartingWindow);
}
- if (!mFrozenBounds.isEmpty()) {
- pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
- pw.print(prefix); pw.print("mFrozenMergedConfig="); pw.println(mFrozenMergedConfig);
- }
if (mPendingRelaunchCount != 0) {
pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
}
@@ -1085,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) {
@@ -1700,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;
@@ -3355,56 +3388,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mPendingRelaunchCount > 0;
}
- boolean shouldFreezeBounds() {
- // For freeform windows, we can't freeze the bounds at the moment because this would make
- // the resizing unresponsive.
- if (task == null || task.inFreeformWindowingMode()) {
- return false;
- }
-
- // We freeze the bounds while drag resizing to deal with the time between
- // the divider/drag handle being released, and the handling it's new
- // configuration. If we are relaunched outside of the drag resizing state,
- // we need to be careful not to do this.
- return task.isDragResizing();
- }
-
@VisibleForTesting
void startRelaunching() {
if (mPendingRelaunchCount == 0) {
mRelaunchStartTime = SystemClock.elapsedRealtime();
}
- if (shouldFreezeBounds()) {
- freezeBounds();
- }
-
clearAllDrawn();
mPendingRelaunchCount++;
}
- /**
- * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
- * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
- * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
- * with a queue.
- */
- private void freezeBounds() {
- mFrozenBounds.offer(new Rect(task.mPreparedFrozenBounds));
-
- if (task.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
- // We didn't call prepareFreezingBounds on the task, so use the current value.
- mFrozenMergedConfig.offer(new Configuration(task.getConfiguration()));
- } else {
- mFrozenMergedConfig.offer(new Configuration(task.mPreparedFrozenMergedConfig));
- }
- // Calling unset() to make it equal to Configuration.EMPTY.
- task.mPreparedFrozenMergedConfig.unset();
- }
-
void finishRelaunching() {
mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
- unfreezeBounds();
if (mPendingRelaunchCount > 0) {
mPendingRelaunchCount--;
@@ -3426,30 +3421,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mPendingRelaunchCount == 0) {
return;
}
- unfreezeBounds();
mPendingRelaunchCount = 0;
mRelaunchStartTime = 0;
}
/**
- * Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
- */
- private void unfreezeBounds() {
- if (mFrozenBounds.isEmpty()) {
- return;
- }
- mFrozenBounds.remove();
- if (!mFrozenMergedConfig.isEmpty()) {
- mFrozenMergedConfig.remove();
- }
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- win.onUnfreezeBounds();
- }
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- }
-
- /**
* Perform clean-up of service connections in an activity record.
*/
private void cleanUpActivityServices() {
@@ -3797,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);
@@ -4445,6 +4421,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mVisibleRequested = visible;
+ setInsetsFrozen(!visible);
if (app != null) {
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
}
@@ -5897,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();
}
@@ -7020,6 +6994,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
float aspect = Math.max(parentWidth, parentHeight)
/ (float) Math.min(parentWidth, parentHeight);
+ // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
+ // set-fixed-orientation-letterbox-aspect-ratio.
+ final float letterboxAspectRatioOverride =
+ mWmService.getFixedOrientationLetterboxAspectRatio();
+ aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? letterboxAspectRatioOverride : aspect;
+
// Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
// order to use the extra available space.
final float maxAspectRatio = info.maxAspectRatio;
@@ -7030,16 +7011,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
aspect = minAspectRatio;
}
- // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
- // TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed
- // orientation letterbox is on the activity level now.
- final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
- // Activity min/max aspect ratio restrictions will be respected by the activity-level
- // letterboxing (size-compat mode). Therefore this override can control the maximum screen
- // area that can be occupied by the app in the letterbox mode.
- aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : aspect;
-
// Store the current bounds to be able to revert to size compat mode values below if needed.
Rect mTmpFullBounds = new Rect(resolvedBounds);
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
@@ -7452,8 +7423,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;
@@ -7517,6 +7487,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.
*/
@@ -8195,7 +8177,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);
@@ -8210,9 +8192,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(STARTING_MOVED, startingMoved);
proto.write(VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW,
mVisibleSetFromTransferredStartingWindow);
- for (Rect bounds : mFrozenBounds) {
- bounds.dumpDebug(proto, FROZEN_BOUNDS);
- }
proto.write(STATE, mState.toString());
proto.write(FRONT_OF_TASK, isRootOfTask());
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 94379b1f230e..e858fe1034b1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -348,13 +348,16 @@ public abstract class ActivityTaskManagerInternal {
public final class ActivityTokens {
private final @NonNull IBinder mActivityToken;
private final @NonNull IBinder mAssistToken;
+ private final @NonNull IBinder mShareableActivityToken;
private final @NonNull IApplicationThread mAppThread;
public ActivityTokens(@NonNull IBinder activityToken,
- @NonNull IBinder assistToken, @NonNull IApplicationThread appThread) {
+ @NonNull IBinder assistToken, @NonNull IApplicationThread appThread,
+ @NonNull IBinder shareableActivityToken) {
mActivityToken = activityToken;
mAssistToken = assistToken;
mAppThread = appThread;
+ mShareableActivityToken = shareableActivityToken;
}
/**
@@ -372,6 +375,13 @@ public abstract class ActivityTaskManagerInternal {
}
/**
+ * @return The sharable activity token..
+ */
+ public @NonNull IBinder getShareableActivityToken() {
+ return mShareableActivityToken;
+ }
+
+ /**
* @return The assist token.
*/
public @NonNull IApplicationThread getApplicationThread() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index db499158a5d9..0c77d9f1f724 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4042,17 +4042,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId) {
- final DisplayContent defaultDisplay =
- mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
-
mTempConfig.setTo(getGlobalConfiguration());
final int changes = mTempConfig.updateFrom(values);
if (changes == 0) {
- // Since calling to Activity.setRequestedOrientation leads to freezing the window with
- // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
- // performDisplayOverrideConfigUpdate in order to send the new display configuration
- // (even if there are no actual changes) to unfreeze the window.
- defaultDisplay.performDisplayOverrideConfigUpdate(values);
return 0;
}
@@ -5550,7 +5542,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return null;
}
return new ActivityTokens(activity.appToken, activity.assistToken,
- activity.app.getThread());
+ activity.app.getThread(), activity.shareableActivityToken);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index bf2aae8867ba..db751e9759fa 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -825,7 +825,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
r.takeOptions(), dc.isNextTransitionForward(),
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
- r.createFixedRotationAdjustmentsIfNeeded()));
+ r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1504,7 +1504,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// Prevent recursion.
return;
}
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+ if (task.isVisible()) {
+ mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+ } else {
+ // Removing a non-visible task doesn't require a transition, but if there is one
+ // collecting, this should be a member just in case.
+ mService.getTransitionController().collect(task);
+ }
task.mInRemoveTask = true;
try {
task.performClearTask(reason);
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
new file mode 100644
index 000000000000..13295e8aca02
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.wm;
+
+import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
+
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.view.ICrossWindowBlurEnabledListener;
+
+final class BlurController {
+
+ private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
+ mBlurEnabledListeners = new RemoteCallbackList<>();
+ private final Object mLock = new Object();
+ boolean mBlurEnabled;
+
+ BlurController() {
+ mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+ }
+
+ boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) {
+ if (listener == null) return false;
+ mBlurEnabledListeners.register(listener);
+ synchronized (mLock) {
+ return mBlurEnabled;
+ }
+ }
+
+ void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) {
+ if (listener == null) return;
+ mBlurEnabledListeners.unregister(listener);
+ }
+
+ private void updateBlurEnabled() {
+ // TODO: add other factors disabling blurs
+ final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+ synchronized (mLock) {
+ if (mBlurEnabled == newEnabled) {
+ return;
+ }
+ mBlurEnabled = newEnabled;
+ notifyBlurEnabledChanged(newEnabled);
+ }
+ }
+
+ private void notifyBlurEnabledChanged(boolean enabled) {
+ int i = mBlurEnabledListeners.beginBroadcast();
+ while (i > 0) {
+ i--;
+ ICrossWindowBlurEnabledListener listener =
+ mBlurEnabledListeners.getBroadcastItem(i);
+ try {
+ listener.onCrossWindowBlurEnabledChanged(enabled);
+ } catch (RemoteException e) {
+ }
+ }
+ mBlurEnabledListeners.finishBroadcast();
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 309b5ec25f0f..62a00802896f 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -141,17 +141,20 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
mMergedOverrideConfiguration);
}
- dispatchConfigurationToChildren();
- }
-
- void dispatchConfigurationToChildren() {
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ConfigurationContainer child = getChildAt(i);
- child.onConfigurationChanged(mFullConfiguration);
+ dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
}
}
/**
+ * Dispatches the configuration to child when {@link #onConfigurationChanged(Configuration)} is
+ * called. This allows the derived classes to override how to dispatch the configuration.
+ */
+ void dispatchConfigurationToChild(E child, Configuration config) {
+ child.onConfigurationChanged(config);
+ }
+
+ /**
* Resolves the current requested override configuration into
* {@link #mResolvedOverrideConfiguration}
*
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 eff4ea6536bd..119ffb732283 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());
@@ -2943,10 +2944,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return dockFrame.bottom - imeFrame.top;
}
- void prepareFreezingTaskBounds() {
- forAllRootTasks(Task::prepareFreezingTaskBounds);
- }
-
void rotateBounds(@Rotation int oldRotation, @Rotation int newRotation, Rect inOutBounds) {
// Get display bounds on oldRotation as parent bounds for the rotation.
getBounds(mTmpRect, oldRotation);
@@ -3703,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.
@@ -4105,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();
}
}
@@ -4833,7 +4823,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
mNoAnimationNotifyOnTransitionFinished.clear();
- mWallpaperController.hideDeferredWallpapersIfNeeded();
+ mWallpaperController.hideDeferredWallpapersIfNeededLegacy();
onAppTransitionDone();
@@ -5272,13 +5262,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW);
}
- static boolean canReuseExistingTask(int windowingMode, int activityType) {
- // Existing Tasks can be reused if a new root task will be created anyway, or for the
- // Dream - because there can only ever be one DreamActivity.
- return alwaysCreateRootTask(windowingMode, activityType)
- || activityType == ACTIVITY_TYPE_DREAM;
- }
-
@Nullable
Task getFocusedRootTask() {
return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedRootTask);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32152ec85493..af9cdeb52fe3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -69,6 +69,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
@@ -242,7 +243,7 @@ public class DisplayPolicy {
}
}
- private final SystemGesturesPointerEventListener mSystemGestures;
+ private SystemGesturesPointerEventListener mSystemGestures;
private volatile int mLidState = LID_ABSENT;
private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -384,7 +385,7 @@ public class DisplayPolicy {
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
- private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+ private GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
@@ -448,119 +449,6 @@ public class DisplayPolicy {
final Looper looper = UiThread.getHandler().getLooper();
mHandler = new PolicyHandler(looper);
- mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
- new SystemGesturesPointerEventListener.Callbacks() {
- @Override
- public void onSwipeFromTop() {
- synchronized (mLock) {
- if (mStatusBar != null) {
- requestTransientBars(mStatusBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
- }
- }
-
- @Override
- public void onSwipeFromBottom() {
- synchronized (mLock) {
- if (mNavigationBar != null
- && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
- }
- }
-
- @Override
- public void onSwipeFromRight() {
- final Region excludedRegion = Region.obtain();
- synchronized (mLock) {
- mDisplayContent.calculateSystemGestureExclusion(
- excludedRegion, null /* outUnrestricted */);
- final boolean excluded =
- mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT
- || !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT);
- }
- excludedRegion.recycle();
- }
-
- @Override
- public void onSwipeFromLeft() {
- final Region excludedRegion = Region.obtain();
- synchronized (mLock) {
- mDisplayContent.calculateSystemGestureExclusion(
- excludedRegion, null /* outUnrestricted */);
- final boolean excluded =
- mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT
- || !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_LEFT);
- }
- excludedRegion.recycle();
- }
-
- @Override
- public void onFling(int duration) {
- if (mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.setPowerBoost(
- Boost.INTERACTION, duration);
- }
- }
-
- @Override
- public void onDebug() {
- // no-op
- }
-
- private WindowOrientationListener getOrientationListener() {
- final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
- return rotation != null ? rotation.getOrientationListener() : null;
- }
-
- @Override
- public void onDown() {
- final WindowOrientationListener listener = getOrientationListener();
- if (listener != null) {
- listener.onTouchStart();
- }
- }
-
- @Override
- public void onUpOrCancel() {
- final WindowOrientationListener listener = getOrientationListener();
- if (listener != null) {
- listener.onTouchEnd();
- }
- }
-
- @Override
- public void onMouseHoverAtTop() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
- msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
- mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
- }
-
- @Override
- public void onMouseHoverAtBottom() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
- msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
- mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
- }
-
- @Override
- public void onMouseLeaveFromEdge() {
- mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
- }
- });
- displayContent.registerPointerEventListener(mSystemGestures);
mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
private Runnable mAppTransitionPending = () -> {
@@ -616,7 +504,7 @@ public class DisplayPolicy {
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
- // TODO: Make it can take screenshot on external display
+ // TODO(b/180986447): Make it can take screenshot on external display
mScreenshotHelper = displayContent.isDefaultDisplay
? new ScreenshotHelper(mContext) : null;
@@ -640,16 +528,6 @@ public class DisplayPolicy {
mRefreshRatePolicy = new RefreshRatePolicy(mService,
mDisplayContent.getDisplayInfo(),
mService.mHighRefreshRateDenylist);
-
- mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
- mContext, () -> {
- synchronized (mLock) {
- onConfigurationChanged();
- mSystemGestures.onConfigurationChanged();
- mDisplayContent.updateSystemGestureExclusion();
- }
- });
- mHandler.post(mGestureNavigationSettingsObserver::register);
}
private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos) {
@@ -668,12 +546,154 @@ public class DisplayPolicy {
}
void systemReady() {
- mSystemGestures.systemReady();
if (mService.mPointerLocationEnabled) {
setPointerLocationEnabled(true);
}
}
+ @NonNull
+ private GestureNavigationSettingsObserver getGestureNavigationSettingsObserver() {
+ if (mGestureNavigationSettingsObserver == null) {
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
+ mContext, () -> {
+ synchronized (mLock) {
+ onConfigurationChanged();
+ getSystemGestures().onConfigurationChanged();
+ mDisplayContent.updateSystemGestureExclusion();
+ }
+ });
+ mHandler.post(mGestureNavigationSettingsObserver::register);
+ }
+ return mGestureNavigationSettingsObserver;
+ }
+
+ @NonNull
+ private SystemGesturesPointerEventListener getSystemGestures() {
+ if (mSystemGestures == null) {
+ final Context gestureContext = mUiContext.createWindowContext(
+ mDisplayContent.getDisplay(), TYPE_POINTER, null /* options */);
+ mSystemGestures = new SystemGesturesPointerEventListener(gestureContext, mHandler,
+ new SystemGesturesPointerEventListener.Callbacks() {
+ @Override
+ public void onSwipeFromTop() {
+ synchronized (mLock) {
+ if (mStatusBar != null) {
+ requestTransientBars(mStatusBar);
+ }
+ checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
+ }
+ }
+
+ @Override
+ public void onSwipeFromBottom() {
+ synchronized (mLock) {
+ if (mNavigationBar != null
+ && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ requestTransientBars(mNavigationBar);
+ }
+ checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
+ }
+ }
+
+ @Override
+ public void onSwipeFromRight() {
+ final Region excludedRegion = Region.obtain();
+ synchronized (mLock) {
+ mDisplayContent.calculateSystemGestureExclusion(
+ excludedRegion, null /* outUnrestricted */);
+ final boolean excluded = mSystemGestures
+ .currentGestureStartedInRegion(excludedRegion);
+ if (mNavigationBar != null
+ && (mNavigationBarPosition == NAV_BAR_RIGHT
+ || !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
+ requestTransientBars(mNavigationBar);
+ }
+ checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT);
+ }
+ excludedRegion.recycle();
+ }
+
+ @Override
+ public void onSwipeFromLeft() {
+ final Region excludedRegion = Region.obtain();
+ synchronized (mLock) {
+ mDisplayContent.calculateSystemGestureExclusion(
+ excludedRegion, null /* outUnrestricted */);
+ final boolean excluded = mSystemGestures
+ .currentGestureStartedInRegion(excludedRegion);
+ if (mNavigationBar != null
+ && (mNavigationBarPosition == NAV_BAR_LEFT
+ || !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
+ requestTransientBars(mNavigationBar);
+ }
+ checkAltBarSwipeForTransientBars(ALT_BAR_LEFT);
+ }
+ excludedRegion.recycle();
+ }
+
+ @Override
+ public void onFling(int duration) {
+ if (mService.mPowerManagerInternal != null) {
+ mService.mPowerManagerInternal.setPowerBoost(
+ Boost.INTERACTION, duration);
+ }
+ }
+
+ @Override
+ public void onDebug() {
+ // no-op
+ }
+
+ private WindowOrientationListener getOrientationListener() {
+ final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
+ return rotation != null ? rotation.getOrientationListener() : null;
+ }
+
+ @Override
+ public void onDown() {
+ final WindowOrientationListener listener = getOrientationListener();
+ if (listener != null) {
+ listener.onTouchStart();
+ }
+ }
+
+ @Override
+ public void onUpOrCancel() {
+ final WindowOrientationListener listener = getOrientationListener();
+ if (listener != null) {
+ listener.onTouchEnd();
+ }
+ }
+
+ @Override
+ public void onMouseHoverAtTop() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+ msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
+ mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+ }
+
+ @Override
+ public void onMouseHoverAtBottom() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
+ msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
+ mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+ }
+
+ @Override
+ public void onMouseLeaveFromEdge() {
+ mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+ }
+ });
+ mDisplayContent.registerPointerEventListener(getSystemGestures());
+ if (mService.mSystemReady) {
+ mSystemGestures.systemReady();
+ }
+ }
+ return mSystemGestures;
+ }
+
private int getDisplayId() {
return mDisplayContent.getDisplayId();
}
@@ -1455,8 +1475,7 @@ public class DisplayPolicy {
}
void onDisplayInfoChanged(DisplayInfo info) {
- mSystemGestures.screenWidth = info.logicalWidth;
- mSystemGestures.screenHeight = info.logicalHeight;
+ getSystemGestures().onDisplayInfoChanged(info);
}
private void layoutStatusBar(DisplayFrames displayFrames, Rect contentFrame) {
@@ -1969,7 +1988,7 @@ public class DisplayPolicy {
public void onOverlayChangedLw() {
updateCurrentUserResources();
onConfigurationChanged();
- mSystemGestures.onConfigurationChanged();
+ getSystemGestures().onConfigurationChanged();
}
/**
@@ -2040,10 +2059,10 @@ public class DisplayPolicy {
}
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
- mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
- mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
- mNavButtonForcedVisible =
- mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+ final GestureNavigationSettingsObserver observer = getGestureNavigationSettingsObserver();
+ mLeftGestureInset = observer.getLeftSensitivity(res);
+ mRightGestureInset = observer.getRightSensitivity(res);
+ mNavButtonForcedVisible = observer.areNavigationButtonForcedVisible();
mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
mNavigationBarAlwaysShowOnSideGesture =
res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture);
@@ -3056,7 +3075,7 @@ public class DisplayPolicy {
}
void release() {
- mHandler.post(mGestureNavigationSettingsObserver::unregister);
+ mHandler.post(getGestureNavigationSettingsObserver()::unregister);
}
@VisibleForTesting
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/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 75176df6aaf7..a971794dc97d 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -124,7 +124,8 @@ class InsetsStateController {
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() :
- target.mAboveInsetsState);
+ (target.mAttrs.receiveInsetsIgnoringZOrder ? mState :
+ target.mAboveInsetsState));
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3f9ea1fd2afd..0e8cadbcbcdd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -650,28 +650,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
@Override
- void dispatchConfigurationToChildren() {
- final Configuration configuration = getConfiguration();
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final DisplayContent displayContent = getChildAt(i);
- if (displayContent.isDefaultDisplay) {
- // The global configuration is also the override configuration of default display.
- displayContent.performDisplayOverrideConfigUpdate(configuration);
- } else {
- displayContent.onConfigurationChanged(configuration);
- }
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- prepareFreezingTaskBounds();
- super.onConfigurationChanged(newParentConfig);
- }
-
- private void prepareFreezingTaskBounds() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- mChildren.get(i).prepareFreezingTaskBounds();
+ void dispatchConfigurationToChild(DisplayContent child, Configuration config) {
+ if (child.isDefaultDisplay) {
+ // The global configuration is also the override configuration of default display.
+ child.performDisplayOverrideConfigUpdate(config);
+ } else {
+ child.onConfigurationChanged(config);
}
}
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index f3859b41b6fd..a98a47802914 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -126,11 +126,17 @@ class SystemGesturesPointerEventListener implements PointerEventListener {
Slog.w(TAG, "Cannot create GestureDetector, display removed:" + displayId);
return;
}
+ onDisplayInfoChanged(info);
mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) {
};
});
}
+ void onDisplayInfoChanged(DisplayInfo info) {
+ screenWidth = info.logicalWidth;
+ screenHeight = info.logicalHeight;
+ }
+
@Override
public void onPointerEvent(MotionEvent event) {
if (mGestureDetector != null && event.isTouchEvent()) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8aa00d0d896f..ea9f2c0e2ae3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -21,6 +21,7 @@ import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -498,9 +499,6 @@ class Task extends WindowContainer<WindowContainer> {
// TODO: Make final
int mUserId;
- final Rect mPreparedFrozenBounds = new Rect();
- final Configuration mPreparedFrozenMergedConfig = new Configuration();
-
// Id of the previous display the root task was on.
int mPrevDisplayId = INVALID_DISPLAY;
@@ -1182,10 +1180,6 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.mNoAnimActivities.add(topActivity);
}
- // We might trigger a configuration change. Save the current task bounds for freezing.
- // TODO: Should this call be moved inside the resize method in WM?
- toRootTask.prepareFreezingTaskBounds();
-
if (toRootTaskWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& moveRootTaskMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) {
// Move recents to front so it is not behind root home task when going into docked
@@ -1260,10 +1254,13 @@ class Task extends WindowContainer<WindowContainer> {
* @param info The activity info which could be different from {@code r.info} if set.
*/
void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) {
- mCallingUid = r.launchedFromUid;
- mCallingPackage = r.launchedFromPackage;
- mCallingFeatureId = r.launchedFromFeatureId;
- setIntent(intent != null ? intent : r.intent, info != null ? info : r.info);
+ if (this.intent == null || !mNeverRelinquishIdentity) {
+ mCallingUid = r.launchedFromUid;
+ mCallingPackage = r.launchedFromPackage;
+ mCallingFeatureId = r.launchedFromFeatureId;
+ setIntent(intent != null ? intent : r.intent, info != null ? info : r.info);
+ return;
+ }
setLockTaskAuth(r);
}
@@ -1271,13 +1268,7 @@ class Task extends WindowContainer<WindowContainer> {
private void setIntent(Intent _intent, ActivityInfo info) {
if (!isLeafTask()) return;
- if (intent == null) {
- mNeverRelinquishIdentity =
- (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
- } else if (mNeverRelinquishIdentity) {
- return;
- }
-
+ mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
affinity = info.taskAffinity;
if (intent == null) {
// If this task already has an intent associated with it, don't set the root
@@ -3366,15 +3357,6 @@ class Task extends WindowContainer<WindowContainer> {
return isResizeable();
}
- /**
- * Prepares the task bounds to be frozen with the current size. See
- * {@link ActivityRecord#freezeBounds}.
- */
- void prepareFreezingBounds() {
- mPreparedFrozenBounds.set(getBounds());
- mPreparedFrozenMergedConfig.setTo(getConfiguration());
- }
-
@Override
void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
Rect outSurfaceInsets) {
@@ -3644,7 +3626,6 @@ class Task extends WindowContainer<WindowContainer> {
@Override
void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
- if (isOrganized()) return;
super.resetSurfacePositionForAnimationLeash(t);
}
@@ -5061,6 +5042,10 @@ class Task extends WindowContainer<WindowContainer> {
}
} else {
// No longer managed by any organizer.
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea != null) {
+ taskDisplayArea.removeLaunchRootTask(this);
+ }
setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
if (mCreatedByOrganizer) {
removeImmediately("setTaskOrganizer");
@@ -7364,6 +7349,7 @@ class Task extends WindowContainer<WindowContainer> {
return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
toTop, null /*activity*/, null /*source*/, null /*options*/);
}
+
// TODO: Can be removed once we change callpoints creating root tasks to be creating tasks.
/** Either returns this current task to be re-used or creates a new child task. */
Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
@@ -7371,7 +7357,7 @@ class Task extends WindowContainer<WindowContainer> {
ActivityRecord source, ActivityOptions options) {
Task task;
- if (DisplayContent.canReuseExistingTask(getWindowingMode(), getActivityType())) {
+ if (canReuseAsLeafTask()) {
// This root task will only contain one task, so just return itself since all root
// tasks ara now tasks and all tasks are now root tasks.
task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
@@ -7406,10 +7392,24 @@ class Task extends WindowContainer<WindowContainer> {
return task;
}
+ /** Return {@code true} if this task can be reused as leaf task. */
+ private boolean canReuseAsLeafTask() {
+ // Cannot be reused as leaf task if this task is created by organizer or having child tasks.
+ if (mCreatedByOrganizer || !isLeafTask()) {
+ return false;
+ }
+
+ // Existing Tasks can be reused if a new root task will be created anyway, or for the
+ // Dream - because there can only ever be one DreamActivity.
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return DisplayContent.alwaysCreateRootTask(windowingMode, activityType)
+ || activityType == ACTIVITY_TYPE_DREAM;
+ }
+
void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
Task task = child.asTask();
try {
-
if (task != null) {
task.setForceShowForAllUsers(showForAllUsers);
}
@@ -7502,10 +7502,6 @@ class Task extends WindowContainer<WindowContainer> {
});
}
- void prepareFreezingTaskBounds() {
- forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
- }
-
private int setBounds(Rect existing, Rect bounds) {
if (equivalentBounds(existing, bounds)) {
return BOUNDS_CHANGE_NONE;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index badd7fda2897..76869e548fce 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1135,12 +1135,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
"Can't set not mCreatedByOrganizer as launch root tr=" + rootTask);
}
- LaunchRootTaskDef def = null;
- for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
- if (mLaunchRootTasks.get(i).task.mTaskId != rootTask.mTaskId) continue;
- def = mLaunchRootTasks.get(i);
- }
-
+ LaunchRootTaskDef def = getLaunchRootTaskDef(rootTask);
if (def != null) {
// Remove so we add to the end of the list.
mLaunchRootTasks.remove(def);
@@ -1156,6 +1151,23 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
}
+ void removeLaunchRootTask(Task rootTask) {
+ LaunchRootTaskDef def = getLaunchRootTaskDef(rootTask);
+ if (def != null) {
+ mLaunchRootTasks.remove(def);
+ }
+ }
+
+ private @Nullable LaunchRootTaskDef getLaunchRootTaskDef(Task rootTask) {
+ LaunchRootTaskDef def = null;
+ for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
+ if (mLaunchRootTasks.get(i).task.mTaskId != rootTask.mTaskId) continue;
+ def = mLaunchRootTasks.get(i);
+ break;
+ }
+ return def;
+ }
+
Task getLaunchRootTask(int windowingMode, int activityType, ActivityOptions options) {
// Try to use the launch root task in options if available.
if (options != null) {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f3b69e30b40a..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))) {
@@ -180,11 +184,13 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
} else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
} else if (layout != null && canApplyFreeformPolicy) {
- getLayoutBounds(display, root, layout, mTmpBounds);
+ mTmpBounds.set(currentParams.mBounds);
+ 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");
@@ -240,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");
@@ -247,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 {
@@ -287,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;
@@ -302,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
@@ -312,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()
@@ -327,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;
}
@@ -499,30 +525,36 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
&& launchMode == WINDOWING_MODE_PINNED;
}
- private void getLayoutBounds(@NonNull DisplayContent display, @NonNull ActivityRecord root,
- @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) {
+ 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;
if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) {
- outBounds.setEmpty();
+ inOutBounds.setEmpty();
return;
}
// 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;
if (!windowLayout.hasSpecifiedSize()) {
- outBounds.setEmpty();
- getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM,
- /* hasInitialBounds */ false, outBounds);
- width = outBounds.width();
- height = outBounds.height();
+ if (!inOutBounds.isEmpty()) {
+ // If the bounds is resolved already and WindowLayout doesn't have any opinion on
+ // its size, use the already resolved size and apply the gravity to it.
+ width = inOutBounds.width();
+ height = inOutBounds.height();
+ } else {
+ getTaskBounds(root, displayArea, windowLayout, WINDOWING_MODE_FREEFORM,
+ /* hasInitialBounds */ false, inOutBounds);
+ width = inOutBounds.width();
+ height = inOutBounds.height();
+ }
} else {
width = defaultWidth;
if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
@@ -563,11 +595,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
fractionOfVerticalOffset = 0.5f;
}
- outBounds.set(0, 0, width, height);
- outBounds.offset(displayStableBounds.left, displayStableBounds.top);
+ inOutBounds.set(0, 0, width, height);
+ inOutBounds.offset(stableBounds.left, stableBounds.top);
final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
- outBounds.offset(xOffset, yOffset);
+ inOutBounds.offset(xOffset, yOffset);
}
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
@@ -575,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) {
@@ -631,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) {
@@ -662,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(
@@ -671,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
@@ -681,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) {
@@ -710,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
@@ -736,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;
@@ -757,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
@@ -774,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;
}
@@ -859,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;
}
@@ -890,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.
@@ -899,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..aadb2722a313 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
@@ -235,16 +240,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer target = mTargets.valueAt(i);
if (target.getParent() != null) {
+ final SurfaceControl targetLeash = getLeashSurface(target);
+ final SurfaceControl origParent = getOrigParentSurface(target);
// Ensure surfaceControls are re-parented back into the hierarchy.
- t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl());
- t.setLayer(target.getSurfaceControl(), target.getLastLayer());
+ t.reparent(targetLeash, origParent);
+ t.setLayer(targetLeash, target.getLastLayer());
// TODO(shell-transitions): Once all remotables have been moved, see if there is
// a more appropriate place to do the following. This may
// involve passing an SF transaction from shell on finish.
target.getRelativePosition(tmpPos);
- t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y);
- t.setCornerRadius(target.getSurfaceControl(), 0);
- t.setShadowRadius(target.getSurfaceControl(), 0);
+ t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
+ t.setCornerRadius(targetLeash, 0);
+ t.setShadowRadius(targetLeash, 0);
displays.add(target.getDisplayContent());
}
}
@@ -271,12 +278,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 */);
}
}
@@ -455,8 +467,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final int depth = getChildDepth(topTargets.valueAt(j), sibling);
if (depth < 0) continue;
if (depth == 0) {
- final int siblingMode = sibling.isVisibleRequested()
- ? TRANSIT_OPEN : TRANSIT_CLOSE;
+ final int siblingMode = changes.get(sibling).getTransitMode(sibling);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" sibling is a top target with mode %s",
TransitionInfo.modeToString(siblingMode));
@@ -638,6 +649,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
+ /** Gets the leash surface for a window container */
+ private static SurfaceControl getLeashSurface(WindowContainer wc) {
+ return wc.getSurfaceControl();
+ }
+
+ private static SurfaceControl getOrigParentSurface(WindowContainer wc) {
+ return wc.getParent().getSurfaceControl();
+ }
+
/**
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
@@ -713,7 +733,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final ChangeInfo info = changes.get(target);
final TransitionInfo.Change change = new TransitionInfo.Change(
target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken()
- : null, target.getSurfaceControl());
+ : null, getLeashSurface(target));
// TODO(shell-transitions): Use leash for non-organized windows.
if (info.mParent != null) {
change.setParent(info.mParent.mRemoteToken.toWindowContainerToken());
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 5f46ffe604a6..6338f39e2e67 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -69,7 +69,7 @@ class TransitionController {
* Creates a transition. It can immediately collect participants.
*/
@NonNull
- Transition createTransition(@WindowManager.TransitionOldType int type,
+ Transition createTransition(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) {
throw new IllegalStateException("Shell Transitions not enabled");
@@ -113,6 +113,14 @@ class TransitionController {
}
/**
+ * @return {@code true} if transition is actively collecting changes and `wc` is one of them.
+ * This is {@code false} once a transition is playing.
+ */
+ boolean isCollecting(@NonNull WindowContainer wc) {
+ return mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc);
+ }
+
+ /**
* @return {@code true} if transition is actively playing. This is not necessarily {@code true}
* during collection.
*/
@@ -128,9 +136,7 @@ class TransitionController {
/** @return {@code true} if wc is in a participant subtree */
boolean inTransition(@NonNull WindowContainer wc) {
- if (mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc)) {
- return true;
- }
+ if (isCollecting(wc)) return true;
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
for (WindowContainer p = wc; p != null; p = p.getParent()) {
if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
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 e183ea0d81ab..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.
@@ -667,4 +667,13 @@ public abstract class WindowManagerInternal {
* Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
*/
public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
+
+ /**
+ * Checks whether the given window should restore the last IME visibility.
+ *
+ * @param imeTargetWindowToken The token of the (IME target) window
+ * @return {@code true} when the system allows to restore the IME visibility,
+ * {@code false} otherwise.
+ */
+ public abstract boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 518176b2ef75..04a560b21da3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -223,6 +223,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.ICrossWindowBlurEnabledListener;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
@@ -462,12 +463,12 @@ public class WindowManagerService extends IWindowManager.Stub
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
/**
- * Override of task letterbox aspect ratio that is set via ADB with
- * set-task-letterbox-aspect-ratio or via {@link
- * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored
+ * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
+ * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
* if it is <= this value.
*/
- static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
+ static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
@VisibleForTesting
WindowManagerConstants mConstants;
@@ -756,6 +757,8 @@ public class WindowManagerService extends IWindowManager.Stub
final TaskSnapshotController mTaskSnapshotController;
+ final BlurController mBlurController = new BlurController();
+
boolean mIsTouchDevice;
boolean mIsFakeTouchDevice;
@@ -1000,9 +1003,9 @@ public class WindowManagerService extends IWindowManager.Stub
private boolean mAnimationsDisabled = false;
boolean mPointerLocationEnabled = false;
- // Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be
- // ignored.
- private volatile float mTaskLetterboxAspectRatio;
+ // Aspect ratio of letterbox for fixed orientation, values <=
+ // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
+ private volatile float mFixedOrientationLetterboxAspectRatio;
/** Enum for Letterbox background type. */
@Retention(RetentionPolicy.SOURCE)
@@ -1253,8 +1256,8 @@ public class WindowManagerService extends IWindowManager.Stub
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
- mTaskLetterboxAspectRatio = context.getResources().getFloat(
- com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
+ mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
mLetterboxActivityCornersRadius = context.getResources().getInteger(
com.android.internal.R.integer.config_letterboxActivityCornersRadius);
mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
@@ -3850,29 +3853,29 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Overrides the aspect ratio of task level letterboxing. If given value is <= {@link
- * #MIN_TASK_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
- * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored and
+ * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
+ * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
- void setTaskLetterboxAspectRatio(float aspectRatio) {
- mTaskLetterboxAspectRatio = aspectRatio;
+ void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+ mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
- * Resets the aspect ratio of task level letterboxing to {@link
- * com.android.internal.R.dimen.config_taskLetterboxAspectRatio}.
+ * Resets the aspect ratio of letterbox for fixed orientation to {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
*/
- void resetTaskLetterboxAspectRatio() {
- mTaskLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
+ void resetFixedOrientationLetterboxAspectRatio() {
+ mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
}
/**
- * Gets the aspect ratio of task level letterboxing.
+ * Gets the aspect ratio of letterbox for fixed orientation.
*/
- float getTaskLetterboxAspectRatio() {
- return mTaskLetterboxAspectRatio;
+ float getFixedOrientationLetterboxAspectRatio() {
+ return mFixedOrientationLetterboxAspectRatio;
}
/**
@@ -3968,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(
@@ -5672,6 +5690,18 @@ public class WindowManagerService extends IWindowManager.Stub
return mWindowTracing.isEnabled();
}
+ @Override
+ public boolean registerCrossWindowBlurEnabledListener(
+ ICrossWindowBlurEnabledListener listener) {
+ return mBlurController.registerCrossWindowBlurEnabledListener(listener);
+ }
+
+ @Override
+ public void unregisterCrossWindowBlurEnabledListener(
+ ICrossWindowBlurEnabledListener listener) {
+ mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
+ }
+
// -------------------------------------------------------------
// Internals
// -------------------------------------------------------------
@@ -6411,6 +6441,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
});
pw.print(" mInTouchMode="); pw.println(mInTouchMode);
+ pw.print(" mBlurEnabled="); pw.println(mBlurController.mBlurEnabled);
pw.print(" mLastDisplayFreezeDuration=");
TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
if ( mLastFinishedFreezeSource != null) {
@@ -8008,6 +8039,11 @@ public class WindowManagerService extends IWindowManager.Stub
return dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getName();
}
}
+
+ @Override
+ public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
+ return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
@@ -8665,6 +8701,22 @@ public class WindowManagerService extends IWindowManager.Stub
boundsInWindow, hashAlgorithm, callback);
}
+ boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
+ synchronized (mGlobalLock) {
+ final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken);
+ if (imeTargetWindow == null) {
+ return false;
+ }
+ final Task imeTargetWindowTask = imeTargetWindow.getTask();
+ if (imeTargetWindowTask == null) {
+ return false;
+ }
+ final TaskSnapshot snapshot = mAtmService.getTaskSnapshot(imeTargetWindowTask.mTaskId,
+ false /* isLowResolution */);
+ return snapshot != null && snapshot.hasImeSurface();
+ }
+ }
+
private void sendDisplayHashError(RemoteCallback callback, int errorCode) {
Bundle bundle = new Bundle();
bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 645786c02fc0..a46a8d56e226 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -117,10 +117,10 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
- case "set-task-letterbox-aspect-ratio":
- return runSetTaskLetterboxAspectRatio(pw);
- case "get-task-letterbox-aspect-ratio":
- return runGetTaskLetterboxAspectRatio(pw);
+ case "set-fixed-orientation-letterbox-aspect-ratio":
+ return runSetFixedOrientationLetterboxAspectRatio(pw);
+ case "get-fixed-orientation-letterbox-aspect-ratio":
+ return runGetFixedOrientationLetterboxAspectRatio(pw);
case "set-letterbox-activity-corners-radius":
return runSetLetterboxActivityCornersRadius(pw);
case "get-letterbox-activity-corners-radius":
@@ -531,12 +531,12 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
- private int runSetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+ private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
final float aspectRatio;
try {
String arg = getNextArgRequired();
if ("reset".equals(arg)) {
- mInternal.resetTaskLetterboxAspectRatio();
+ mInternal.resetFixedOrientationLetterboxAspectRatio();
return 0;
}
aspectRatio = Float.parseFloat(arg);
@@ -549,13 +549,13 @@ public class WindowManagerShellCommand extends ShellCommand {
return -1;
}
- mInternal.setTaskLetterboxAspectRatio(aspectRatio);
+ mInternal.setFixedOrientationLetterboxAspectRatio(aspectRatio);
return 0;
}
- private int runGetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
- final float aspectRatio = mInternal.getTaskLetterboxAspectRatio();
- if (aspectRatio <= WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO) {
+ private int runGetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+ final float aspectRatio = mInternal.getFixedOrientationLetterboxAspectRatio();
+ if (aspectRatio <= WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
pw.println("Letterbox aspect ratio is not set");
} else {
pw.println("Letterbox aspect ratio is " + aspectRatio);
@@ -692,8 +692,8 @@ public class WindowManagerShellCommand extends ShellCommand {
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
- // set-task-letterbox-aspect-ratio
- mInternal.resetTaskLetterboxAspectRatio();
+ // set-fixed-orientation-letterbox-aspect-ratio
+ mInternal.resetFixedOrientationLetterboxAspectRatio();
// set-letterbox-activity-corners-radius
mInternal.resetLetterboxActivityCornersRadius();
@@ -734,12 +734,12 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
- pw.println(" set-task-letterbox-aspect-ratio [reset|aspectRatio]");
- pw.println(" get-task-letterbox-aspect-ratio");
- pw.println(" Aspect ratio of task level letterboxing. If aspectRatio <= "
- + WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO);
- pw.println(" both it and R.dimen.config_taskLetterboxAspectRatio will be ignored");
- pw.println(" and framework implementation will be used to determine aspect ratio.");
+ pw.println(" set-fixed-orientation-letterbox-aspect-ratio [reset|aspectRatio]");
+ pw.println(" get-fixed-orientation-letterbox-aspect-ratio");
+ pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+ + WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will be");
+ pw.println(" ignored and framework implementation will determine aspect ratio.");
pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]");
pw.println(" get-letterbox-activity-corners-radius");
pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1fc7041c0fe2..1830c0758f9a 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,
@@ -1190,16 +1187,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
layoutYDiff = 0;
} else {
windowFrames.mContainingFrame.set(getBounds());
- if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) {
-
- // If the bounds are frozen, we still want to translate the window freely and only
- // freeze the size.
- Rect frozen = mActivityRecord.mFrozenBounds.peek();
- windowFrames.mContainingFrame.right =
- windowFrames.mContainingFrame.left + frozen.width();
- windowFrames.mContainingFrame.bottom =
- windowFrames.mContainingFrame.top + frozen.height();
- }
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (isImeTarget) {
if (inFreeformWindowingMode()) {
@@ -1725,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();
}
/**
@@ -1755,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();
}
/**
@@ -1814,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);
}
@@ -1953,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);
}
@@ -2068,23 +2067,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
super.onResize();
}
- void onUnfreezeBounds() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- c.onUnfreezeBounds();
- }
-
- if (!mHasSurface) {
- return;
- }
-
- mLayoutNeeded = true;
- setDisplayLayoutNeeded();
- if (!mWmService.mResizingWindows.contains(this)) {
- mWmService.mResizingWindows.add(this);
- }
- }
-
/**
* If the window has moved due to its containing content frame changing, then notify the
* listeners and optionally animate it. Simply checking a change of position is not enough,
@@ -3251,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;
@@ -3579,10 +3565,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public Configuration getConfiguration() {
- if (mActivityRecord != null && mActivityRecord.mFrozenMergedConfig.size() > 0) {
- return mActivityRecord.mFrozenMergedConfig.peek();
- }
-
// If the process has not registered to any display area to listen to the configuration
// change, we can simply return the mFullConfiguration as default.
if (!registeredForDisplayAreaConfigChanges()) {
@@ -3639,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;
}
@@ -3944,14 +3928,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return true;
}
- // If the bounds are currently frozen, it means that the layout size that the app sees
- // and the bounds we clip this window to might be different. In order to avoid holes, we
- // simulate that we are still resizing so the app fills the hole with the resizing
- // background.
- return (getDisplayContent().mDividerControllerLocked.isResizing()
- || mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWindowingMode() && !isGoneForLayout();
-
+ return getDisplayContent().mDividerControllerLocked.isResizing()
+ && !task.inFreeformWindowingMode() && !isGoneForLayout();
}
void setDragResizing() {
@@ -4061,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);
@@ -4876,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);
@@ -5273,22 +5195,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || (mAttrs.flags & FLAG_BLUR_BEHIND) != 0)
+ } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
&& isVisibleNow() && !mHidden) {
// Only show the Dimmer when the following is satisfied:
- // 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested
+ // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
// 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
// 3. The WS is considered visible according to the isVisible() method
// 4. The WS is not hidden.
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
- final int blurRadius =
- (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 ? mAttrs.blurBehindRadius : 0;
- getDimmer().dimBelow(
- getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.blurBehindRadius);
+ final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
+ getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius);
}
}
+ private boolean shouldDrawBlurBehind() {
+ return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mWmService.mBlurController.mBlurEnabled;
+ }
+
/**
* Notifies SF about the priority of the window, if it changed. SF then uses this information
* to decide which window's desired rendering rate should have a priority when deciding about
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/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 7b78b8d23b4b..8efbaf577134 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -39,6 +39,8 @@
#include "dataloader.h"
+// #define VERBOSE_READ_LOGS
+
namespace android {
namespace {
@@ -631,28 +633,65 @@ private:
};
void onPageReadsWithUid(dataloader::PageReadsWithUid pageReads) final {
+ if (!pageReads.size()) {
+ return;
+ }
+
auto trace = atrace_is_tag_enabled(ATRACE_TAG);
if (CC_LIKELY(!trace)) {
return;
}
TracedRead last = {};
+ auto lastSerialNo = mLastSerialNo;
for (auto&& read : pageReads) {
- if (read.id != last.fileId || read.uid != last.uid ||
- read.block != last.firstBlockIdx + last.count) {
- traceRead(last);
- last = TracedRead{
- .timestampUs = read.bootClockTsUs,
- .fileId = read.id,
- .uid = read.uid,
- .firstBlockIdx = (uint32_t)read.block,
- .count = 1,
- };
- } else {
+ const auto expectedSerialNo = lastSerialNo + last.count;
+#ifdef VERBOSE_READ_LOGS
+ {
+ FileIdx fileIdx = convertFileIdToFileIndex(read.id);
+
+ auto appId = multiuser_get_app_id(read.uid);
+ auto userId = multiuser_get_user_id(read.uid);
+ auto trace = android::base::
+ StringPrintf("verbose_page_read: serialNo=%lld (expected=%lld) index=%lld "
+ "file=%d appid=%d userid=%d",
+ static_cast<long long>(read.serialNo),
+ static_cast<long long>(expectedSerialNo),
+ static_cast<long long>(read.block), static_cast<int>(fileIdx),
+ static_cast<int>(appId), static_cast<int>(userId));
+
+ ATRACE_BEGIN(trace.c_str());
+ ATRACE_END();
+ }
+#endif // VERBOSE_READ_LOGS
+
+ if (read.serialNo == expectedSerialNo && read.id == last.fileId &&
+ read.uid == last.uid && read.block == last.firstBlockIdx + last.count) {
++last.count;
+ continue;
+ }
+
+ // First, trace the reads.
+ traceRead(last);
+
+ // Second, report missing reads, if any.
+ if (read.serialNo != expectedSerialNo) {
+ const auto readsMissing = read.serialNo - expectedSerialNo;
+ traceMissingReads(readsMissing);
}
+
+ last = TracedRead{
+ .timestampUs = read.bootClockTsUs,
+ .fileId = read.id,
+ .uid = read.uid,
+ .firstBlockIdx = (uint32_t)read.block,
+ .count = 1,
+ };
+ lastSerialNo = read.serialNo;
}
+
traceRead(last);
+ mLastSerialNo = lastSerialNo + last.count;
}
void traceRead(const TracedRead& read) {
@@ -682,6 +721,13 @@ private:
ATRACE_END();
}
+ void traceMissingReads(int64_t count) {
+ const auto trace = android::base::StringPrintf("missing_page_reads: count=%lld",
+ static_cast<long long>(count));
+ ATRACE_BEGIN(trace.c_str());
+ ATRACE_END();
+ }
+
void receiver(unique_fd inout, MetadataMode mode) {
std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions;
@@ -828,6 +874,7 @@ private:
std::atomic<bool> mStopReceiving = false;
std::atomic<bool> mReadLogsEnabled = false;
std::chrono::milliseconds mWaitOnEofInterval{WaitOnEofMinInterval};
+ int64_t mLastSerialNo{1};
/** Tracks which files have been requested */
std::unordered_set<FileIdx> mRequestedFiles;
};
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 89b931d35c40..ef2d0baff031 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -238,12 +238,12 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j
return result.isOk() ? result.value().count() : -1;
}
-static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray composition, jlong vibrationId) {
+static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobjectArray composition, jlong vibrationId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized");
- return;
+ return -1;
}
size_t size = env->GetArrayLength(composition);
std::vector<aidl::CompositeEffect> effects;
@@ -252,7 +252,8 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong
effects.push_back(effectFromJavaPrimitive(env, element));
}
auto callback = wrapper->createCallback(vibrationId);
- wrapper->hal()->performComposedEffect(effects, callback);
+ auto result = wrapper->hal()->performComposedEffect(effects, callback);
+ return result.isOk() ? result.value().count() : -1;
}
static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) {
@@ -296,7 +297,7 @@ static const JNINativeMethod method_table[] = {
{"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
{"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
{"vibratorPerformComposedEffect",
- "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V",
+ "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J",
(void*)vibratorPerformComposedEffect},
{"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
{"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
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/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 0d878b401bee..a262939c0ef9 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -102,7 +102,7 @@ public final class ProfcollectForwardingService extends SystemService {
return false;
}
try {
- return !mIProfcollect.GetSupportedProvider().isEmpty();
+ return !mIProfcollect.get_supported_provider().isEmpty();
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
return false;
@@ -191,7 +191,7 @@ public final class ProfcollectForwardingService extends SystemService {
}
try {
- sSelfService.mIProfcollect.ProcessProfile();
+ sSelfService.mIProfcollect.process(false);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
@@ -234,7 +234,7 @@ public final class ProfcollectForwardingService extends SystemService {
if (DEBUG) {
Log.d(LOG_TAG, "Tracing on app launch event: " + packageName);
}
- mIProfcollect.TraceOnce("applaunch");
+ mIProfcollect.trace_once("applaunch");
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
@@ -296,7 +296,7 @@ public final class ProfcollectForwardingService extends SystemService {
}
try {
- mIProfcollect.CreateProfileReport();
+ mIProfcollect.report();
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index e99b07144853..0fa9a1def381 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -52,8 +52,10 @@ class DomainVerificationCollectorTest {
val collector = mockCollector()
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg))
+ assertThat(collector.collectValidAutoVerifyDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg))
+ .containsExactly("invalid1", "invalid2", "invalid3", "invalid4")
}
@Test
@@ -62,7 +64,8 @@ class DomainVerificationCollectorTest {
val collector = mockCollector()
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectValidAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg)).isEmpty()
}
@Test
@@ -71,8 +74,10 @@ class DomainVerificationCollectorTest {
val collector = mockCollector(linkedApps = setOf(TEST_PKG_NAME))
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg))
+ assertThat(collector.collectValidAutoVerifyDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com", "example4.com")
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg))
+ .containsExactly("invalid1", "invalid2", "invalid3", "invalid4")
}
@Test
@@ -92,6 +97,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example1.com", null)
+ addDataAuthority("invalid1", null)
}
)
},
@@ -111,6 +117,7 @@ class DomainVerificationCollectorTest {
addDataScheme("nonWebScheme")
addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example2.com", null)
+ addDataAuthority("invalid2", null)
}
)
},
@@ -122,7 +129,8 @@ class DomainVerificationCollectorTest {
val collector = mockCollector()
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com")
- assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectValidAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg)).isEmpty()
}
@Test
@@ -132,8 +140,10 @@ class DomainVerificationCollectorTest {
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg))
+ assertThat(collector.collectValidAutoVerifyDomains(pkg))
.containsExactly("example1.com", "example3.com")
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg))
+ .containsExactly("invalid1", "invalid3")
}
@Test
@@ -143,7 +153,8 @@ class DomainVerificationCollectorTest {
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectValidAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg)).isEmpty()
}
@Test
@@ -153,7 +164,8 @@ class DomainVerificationCollectorTest {
assertThat(collector.collectAllWebDomains(pkg))
.containsExactly("example1.com", "example2.com", "example3.com")
- assertThat(collector.collectAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectValidAutoVerifyDomains(pkg)).isEmpty()
+ assertThat(collector.collectInvalidAutoVerifyDomains(pkg)).isEmpty()
}
private fun mockCollector(linkedApps: Set<String> = emptySet()): DomainVerificationCollector {
@@ -178,6 +190,7 @@ class DomainVerificationCollectorTest {
<data android:scheme="https"/>
<data android:path="/sub"/>
<data android:host="example1.com"/>
+ <data android:host="invalid1"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
@@ -186,6 +199,7 @@ class DomainVerificationCollectorTest {
<data android:scheme="http"/>
<data android:path="/sub2"/>
<data android:host="example2.com"/>
+ <data android:host="invalid2."/>
</intent-filter>
<intent-filter android:autoVerify="$autoVerify">
<action android:name="android.intent.action.VIEW"/>
@@ -194,6 +208,7 @@ class DomainVerificationCollectorTest {
<data android:scheme="https"/>
<data android:path="/sub3"/>
<data android:host="example3.com"/>
+ <data android:host=".invalid3"/>
</intent-filter>
<intent-filter android:autoVerify="$autoVerify">
<action android:name="android.intent.action.VIEW"/>
@@ -201,6 +216,7 @@ class DomainVerificationCollectorTest {
<data android:scheme="https"/>
<data android:path="/sub4"/>
<data android:host="example4.com"/>
+ <data android:host="invalid4"/>
</intent-filter>
<intent-filter android:autoVerify="$autoVerify">
<action android:name="android.intent.action.VIEW"/>
@@ -208,6 +224,7 @@ class DomainVerificationCollectorTest {
<data android:scheme="https"/>
<data android:path="/sub5"/>
<data android:host="example5.com"/>
+ <data android:host="invalid5"/>
</intent-filter>
<intent-filter android:autoVerify="$autoVerify">
<category android:name="android.intent.category.BROWSABLE"/>
@@ -215,11 +232,12 @@ class DomainVerificationCollectorTest {
<data android:scheme="https"/>
<data android:path="/sub5"/>
<data android:host="example5.com"/>
+ <data android:host="invalid5"/>
</intent-filter>
</xml>
""".trimIndent()
- return mockThrowOnUnmocked<AndroidPackage> {
+ return mockThrowOnUnmocked {
whenever(packageName) { TEST_PKG_NAME }
whenever(targetSdkVersion) {
if (useV2) Build.VERSION_CODES.S else Build.VERSION_CODES.R
@@ -238,6 +256,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example1.com", null)
+ addDataAuthority("invalid1", null)
}
)
addIntent(
@@ -248,6 +267,7 @@ class DomainVerificationCollectorTest {
addDataScheme("http")
addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example2.com", null)
+ addDataAuthority("invalid2", null)
}
)
},
@@ -261,6 +281,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub3", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example3.com", null)
+ addDataAuthority("invalid3", null)
}
)
},
@@ -273,6 +294,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub4", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example4.com", null)
+ addDataAuthority("invalid4", null)
}
)
addIntent(
@@ -283,6 +305,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub5", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example5.com", null)
+ addDataAuthority("invalid5", null)
}
)
addIntent(
@@ -293,6 +316,7 @@ class DomainVerificationCollectorTest {
addDataScheme("https")
addDataPath("/sub6", PatternMatcher.PATTERN_LITERAL)
addDataAuthority("example6.com", null)
+ addDataAuthority("invalid6", null)
}
)
},
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..6597577cf14f 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,14 +22,15 @@ 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
import org.junit.Test
import org.junit.rules.TemporaryFolder
+import org.xmlpull.v1.XmlPullParser
import java.io.File
import java.nio.charset.StandardCharsets
import java.util.UUID
@@ -41,21 +42,41 @@ class DomainVerificationPersistenceTest {
internal fun File.writeXml(block: (serializer: TypedXmlSerializer) -> Unit) = apply {
outputStream().use {
- // Explicitly use string based XML so it can printed in the test failure output
- Xml.newFastSerializer()
+ // This must use the binary serializer the mirror the production behavior, as
+ // there are slight differences with the string based one.
+ Xml.newBinarySerializer()
.apply {
setOutput(it, StandardCharsets.UTF_8.name())
startDocument(null, true)
setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true)
+ // Write a wrapping tag to ensure the domain verification settings didn't
+ // close out the document, allowing other settings to be written
+ startTag(null, "wrapper-tag")
}
.apply(block)
+ .apply {
+ startTag(null, "trailing-tag")
+ endTag(null, "trailing-tag")
+ endTag(null, "wrapper-tag")
+ }
.endDocument()
}
}
internal fun <T> File.readXml(block: (parser: TypedXmlPullParser) -> T) =
inputStream().use {
- block(Xml.resolvePullParser(it))
+ val parser = Xml.resolvePullParser(it)
+ assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+ assertThat(parser.name).isEqualTo("wrapper-tag")
+ assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+ block(parser).also {
+ assertThat(parser.nextTag()).isEqualTo(XmlPullParser.START_TAG)
+ assertThat(parser.name).isEqualTo("trailing-tag")
+ assertThat(parser.nextTag()).isEqualTo(XmlPullParser.END_TAG)
+ assertThat(parser.name).isEqualTo("trailing-tag")
+ assertThat(parser.nextTag()).isEqualTo(XmlPullParser.END_TAG)
+ assertThat(parser.name).isEqualTo("wrapper-tag")
+ }
}
}
@@ -102,14 +123,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 +235,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/DomainVerificationProxyTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
index db541f672954..91e5beccee09 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationProxyTest.kt
@@ -122,7 +122,7 @@ class DomainVerificationProxyTest {
whenever(setDomainVerificationStatusInternal(anyInt(), any(), any(), anyInt()))
}
collector = mockThrowOnUnmocked {
- whenever(collectAutoVerifyDomains(any())) {
+ whenever(collectValidAutoVerifyDomains(any())) {
when (val pkgName = (arguments[0] as AndroidPackage).packageName) {
TEST_PKG_NAME_TARGET_ONE -> ArraySet(setOf("example1.com", "example2.com"))
TEST_PKG_NAME_TARGET_TWO -> ArraySet(setOf("example3.com", "example4.com"))
@@ -477,7 +477,7 @@ class DomainVerificationProxyTest {
whenever(
addPowerSaveTempWhitelistApp(
anyInt(), anyString(), anyLong(), anyInt(),
- anyBoolean(), anyString()
+ anyBoolean(), anyInt(), anyString()
)
)
}
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/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 18184b0a82af..f1d8e6c167d7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -592,6 +592,7 @@ public class LocalDisplayAdapterTest {
new DisplayModeDirector.RefreshRateRange(60f, 60f),
new DisplayModeDirector.RefreshRateRange(60f, 60f)
));
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token,
new SurfaceControl.DesiredDisplayModeSpecs(
/* baseModeId */ 0,
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/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
new file mode 100644
index 000000000000..489e2f769a3d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for {@link com.android.server.BootReceiver}
+ */
+public class BootReceiverTest extends AndroidTestCase {
+ public void testLogLinePotentiallySensitive() throws Exception {
+ /*
+ * Strings to be dropped from the log as potentially sensitive: register dumps, process
+ * names, hardware info.
+ */
+ final String[] becomeNull = {
+ "CPU: 4 PID: 120 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7",
+ "Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014",
+ "[ 0.083207] RSP: 0000:ffffffff8fe07ca8 EFLAGS: 00010046 ORIG_RAX: 0000000000000000",
+ "[ 0.084709] RAX: 0000000000000000 RBX: ffffffffff240000 RCX: ffffffff815fcf01",
+ "[ 0.086109] RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffffffffff240004",
+ "[ 0.087509] RBP: ffffffff8fe07d60 R08: fffffbfff1fc0f21 R09: fffffbfff1fc0f21",
+ "[ 0.088911] R10: ffffffff8fe07907 R11: fffffbfff1fc0f20 R12: ffffffff8fe07d38",
+ "R13: 0000000000000001 R14: 0000000000000001 R15: ffffffff8fe07e80",
+ "x29: ffff00003ce07150 x28: ffff80001aa29cc0",
+ "x1 : 0000000000000000 x0 : ffff00000f628000",
+ };
+
+ /* Strings to be left unchanged, including non-sensitive registers and parts of reports. */
+ final String[] leftAsIs = {
+ "FS: 0000000000000000(0000) GS:ffffffff92409000(0000) knlGS:0000000000000000",
+ "[ 69.2366] [ T6006]c7 6006 =======================================================",
+ "[ 69.245688] [ T6006] BUG: KFENCE: out-of-bounds in kfence_handle_page_fault",
+ "[ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 ",
+ "[ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c",
+ "pc : __mutex_lock+0x428/0x99c ",
+ "sp : ffff00003ce07150",
+ "Call trace:",
+ "",
+ };
+
+ final String[][] stripped = {
+ { "Detected corrupted memory at 0xffffffffb6797ff9 [ 0xac . . . . . . ]:",
+ "Detected corrupted memory at 0xffffffffb6797ff9" },
+ };
+ for (int i = 0; i < becomeNull.length; i++) {
+ assertEquals(BootReceiver.stripSensitiveData(becomeNull[i]), null);
+ }
+
+ for (int i = 0; i < leftAsIs.length; i++) {
+ assertEquals(BootReceiver.stripSensitiveData(leftAsIs[i]), leftAsIs[i]);
+ }
+
+ for (int i = 0; i < stripped.length; i++) {
+ assertEquals(BootReceiver.stripSensitiveData(stripped[i][0]), stripped[i][1]);
+ }
+ }
+}
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..d6c11a549dfa 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;
@@ -493,7 +495,8 @@ public class AbstractAccessibilityServiceConnectionTest {
mServiceConnection.performAccessibilityAction(PIP_WINDOWID, ROOT_NODE_ID,
ACTION_ACCESSIBILITY_FOCUS, null, INTERACTION_ID, mMockCallback, TID);
- verify(mMockIPowerManager).userActivity(anyLong(), anyInt(), anyInt());
+ verify(mMockIPowerManager).userActivity(eq(Display.DEFAULT_DISPLAY), anyLong(), anyInt(),
+ anyInt());
verify(mMockIA11yInteractionConnection).performAccessibilityAction(eq(ROOT_NODE_ID),
eq(ACTION_ACCESSIBILITY_FOCUS), any(), eq(INTERACTION_ID), eq(mMockCallback),
anyInt(), eq(PID), eq(TID));
@@ -849,12 +852,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/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
new file mode 100644
index 000000000000..170f561aa2da
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -0,0 +1,581 @@
+/*
+ * 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;
+
+
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS;
+import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.AccessibilityInteractionController;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeIdManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests that verify expected node and prefetched node results when finding a view by node id. We
+ * send some requests to the controller via View methods to control message timing.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityInteractionControllerNodeRequestsTest {
+ private AccessibilityInteractionController mAccessibilityInteractionController;
+ @Mock
+ private IAccessibilityInteractionConnectionCallback mMockClientCallback1;
+ @Mock
+ private IAccessibilityInteractionConnectionCallback mMockClientCallback2;
+
+ @Captor
+ private ArgumentCaptor<AccessibilityNodeInfo> mFindInfoCaptor;
+ @Captor private ArgumentCaptor<List<AccessibilityNodeInfo>> mPrefetchInfoListCaptor;
+
+ private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private static final int MOCK_CLIENT_1_THREAD_AND_PROCESS_ID = 1;
+ private static final int MOCK_CLIENT_2_THREAD_AND_PROCESS_ID = 2;
+
+ private static final String FRAME_LAYOUT_DESCRIPTION = "frameLayout";
+ private static final String TEXT_VIEW_1_DESCRIPTION = "textView1";
+ private static final String TEXT_VIEW_2_DESCRIPTION = "textView2";
+
+ private TestFrameLayout mFrameLayout;
+ private TestTextView mTextView1;
+ private TestTextView2 mTextView2;
+
+ private boolean mSendClient1RequestForTextAfterTextPrefetched;
+ private boolean mSendClient2RequestForTextAfterTextPrefetched;
+ private boolean mSendRequestForTextAndIncludeUnImportantViews;
+ private int mMockClient1InteractionId;
+ private int mMockClient2InteractionId;
+
+ @Before
+ public void setUp() throws Throwable {
+ MockitoAnnotations.initMocks(this);
+
+ mInstrumentation.runOnMainSync(() -> {
+ final Context context = mInstrumentation.getTargetContext();
+ final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+
+ mFrameLayout = new TestFrameLayout(context);
+ mTextView1 = new TestTextView(context);
+ mTextView2 = new TestTextView2(context);
+
+ mFrameLayout.addView(mTextView1);
+ mFrameLayout.addView(mTextView2);
+
+ // The controller retrieves views through this manager, and registration happens on
+ // when attached to a window, which we don't have. We can simply reference FrameLayout
+ // with ROOT_NODE_ID
+ AccessibilityNodeIdManager.getInstance().registerViewWithId(
+ mTextView1, mTextView1.getAccessibilityViewId());
+ AccessibilityNodeIdManager.getInstance().registerViewWithId(
+ mTextView2, mTextView2.getAccessibilityViewId());
+
+ try {
+ viewRootImpl.setView(mFrameLayout, new WindowManager.LayoutParams(), null);
+
+ } catch (WindowManager.BadTokenException e) {
+ // activity isn't running, we will ignore BadTokenException.
+ }
+
+ mAccessibilityInteractionController =
+ new AccessibilityInteractionController(viewRootImpl);
+ });
+
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ AccessibilityNodeIdManager.getInstance().unregisterViewWithId(
+ mTextView1.getAccessibilityViewId());
+ AccessibilityNodeIdManager.getInstance().unregisterViewWithId(
+ mTextView2.getAccessibilityViewId());
+ }
+
+ /**
+ * Tests a basic request for the root node with prefetch flag
+ * {@link AccessibilityNodeInfo#FLAG_PREFETCH_DESCENDANTS}
+ *
+ * @throws RemoteException
+ */
+ @Test
+ public void testFindRootView_withOneClient_shouldReturnRootNodeAndPrefetchDescendants()
+ throws RemoteException {
+ // Request for our FrameLayout
+ sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+ mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+ mInstrumentation.waitForIdleSync();
+
+ // Verify we get FrameLayout
+ verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+ mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+ AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+ assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription());
+
+ verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(
+ mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId));
+ // The descendants are our two TextViews
+ List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+ assertEquals(2, prefetchedNodes.size());
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+ assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(1).getContentDescription());
+
+ }
+
+ /**
+ * Tests a basic request for TestTextView1's node with prefetch flag
+ * {@link AccessibilityNodeInfo#FLAG_PREFETCH_SIBLINGS}
+ *
+ * @throws RemoteException
+ */
+ @Test
+ public void testFindTextView_withOneClient_shouldReturnNodeAndPrefetchedSiblings()
+ throws RemoteException {
+ // Request for TextView1
+ sendNodeRequestToController(AccessibilityNodeInfo.makeNodeId(
+ mTextView1.getAccessibilityViewId(), AccessibilityNodeProvider.HOST_VIEW_ID),
+ mMockClientCallback1, mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS);
+ mInstrumentation.waitForIdleSync();
+
+ // Verify we get TextView1
+ verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+ mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+ AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription());
+
+ // Verify the prefetched sibling of TextView1 is TextView2
+ verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(
+ mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId));
+ // TextView2 is the prefetched sibling
+ List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+ assertEquals(1, prefetchedNodes.size());
+ assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+ }
+
+ /**
+ * Tests a series of controller requests to prevent prefetching.
+ * Request 1: Client 1 requests the root node
+ * Request 2: When the root node is initialized in
+ * {@link TestFrameLayout#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)},
+ * Client 2 requests TestTextView1's node
+ *
+ * Request 2 on the queue prevents prefetching for Request 1.
+ *
+ * @throws RemoteException
+ */
+ @Test
+ public void testFindRootAndTextNodes_withTwoClients_shouldPreventClient1Prefetch()
+ throws RemoteException {
+ mFrameLayout.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ final long nodeId = AccessibilityNodeInfo.makeNodeId(
+ mTextView1.getAccessibilityViewId(),
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+
+ // Enqueue a request when this node is found from a different service for
+ // TextView1
+ sendNodeRequestToController(nodeId, mMockClientCallback2,
+ mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS);
+ }
+ });
+ // Client 1 request for FrameLayout
+ sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+ mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+ mInstrumentation.waitForIdleSync();
+
+ // Verify client 1 gets FrameLayout
+ verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult(
+ mFindInfoCaptor.capture(), eq(mMockClient1InteractionId));
+ AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue();
+ assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription());
+
+ // The second request is put in the queue in the FrameLayout's onInitializeA11yNodeInfo,
+ // meaning prefetching is interrupted and does not even begin for the first request
+ verify(mMockClientCallback1, never())
+ .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt());
+
+ // Verify client 2 gets TextView1
+ verify(mMockClientCallback2).setFindAccessibilityNodeInfoResult(
+ mFindInfoCaptor.capture(), eq(mMockClient2InteractionId));
+ infoSentToService = mFindInfoCaptor.getValue();
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription());
+
+ // Verify the prefetched sibling of TextView1 is TextView2 (FLAG_PREFETCH_SIBLINGS)
+ verify(mMockClientCallback2).setPrefetchAccessibilityNodeInfoResult(
+ mPrefetchInfoListCaptor.capture(), eq(mMockClient2InteractionId));
+ List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+ assertEquals(1, prefetchedNodes.size());
+ assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+ }
+
+ /**
+ * Tests a series of controller same-service requests to interrupt prefetching and satisfy a
+ * pending node request.
+ * Request 1: Request the root node
+ * Request 2: When TextTextView1's node is initialized as part of Request 1's prefetching,
+ * request TestTextView1's node
+ *
+ * Request 1 prefetches TestTextView1's node, is interrupted by a pending request, and checks
+ * if its prefetched nodes satisfy any pending requests. It satisfies Request 2's request for
+ * TestTextView1's node. Request 2 is fulfilled, so it is removed from queue and does not
+ * prefetch.
+ *
+ * @throws RemoteException
+ */
+ @Test
+ public void testFindRootAndTextNode_withOneClient_shouldInterruptPrefetchAndSatisfyPendingMsg()
+ throws RemoteException {
+ mSendClient1RequestForTextAfterTextPrefetched = true;
+
+ mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+ final long nodeId = AccessibilityNodeInfo.makeNodeId(
+ mTextView1.getAccessibilityViewId(),
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+
+ if (mSendClient1RequestForTextAfterTextPrefetched) {
+ // Prevent a loop when processing second request
+ mSendClient1RequestForTextAfterTextPrefetched = false;
+ // TextView1 is prefetched here after the FrameLayout is found. Now enqueue a
+ // same-client request for TextView1
+ sendNodeRequestToController(nodeId, mMockClientCallback1,
+ ++mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS);
+
+ }
+ }
+ });
+ // Client 1 requests FrameLayout
+ sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+ mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+ // Flush out all messages
+ mInstrumentation.waitForIdleSync();
+
+ // When TextView1 is prefetched for FrameLayout, we put a message on the queue in
+ // TextView1's onInitializeA11yNodeInfo that requests for TextView1. The service thus get
+ // two node results for FrameLayout and TextView1.
+ verify(mMockClientCallback1, times(2))
+ .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+
+ List<AccessibilityNodeInfo> foundNodes = mFindInfoCaptor.getAllValues();
+ assertEquals(FRAME_LAYOUT_DESCRIPTION, foundNodes.get(0).getContentDescription());
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, foundNodes.get(1).getContentDescription());
+
+ // The controller will look at FrameLayout's prefetched nodes and find matching nodes in
+ // pending requests. The prefetched TextView1 matches the second request. The second
+ // request was removed from queue and prefetching for this request never occurred.
+ verify(mMockClientCallback1, times(1))
+ .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+ eq(mMockClient1InteractionId - 1));
+ List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+ assertEquals(1, prefetchedNodes.size());
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+ }
+
+ /**
+ * Like above, but tests a series of controller requests from different services to interrupt
+ * prefetching and satisfy a pending node request.
+ *
+ * @throws RemoteException
+ */
+ @Test
+ public void testFindRootAndTextNode_withTwoClients_shouldInterruptPrefetchAndSatisfyPendingMsg()
+ throws RemoteException {
+ mSendClient2RequestForTextAfterTextPrefetched = true;
+ mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+ final long nodeId = AccessibilityNodeInfo.makeNodeId(
+ mTextView1.getAccessibilityViewId(),
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+
+ if (mSendClient2RequestForTextAfterTextPrefetched) {
+ mSendClient2RequestForTextAfterTextPrefetched = false;
+ // TextView1 is prefetched here. Now enqueue client 2's request for
+ // TextView1
+ sendNodeRequestToController(nodeId, mMockClientCallback2,
+ mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS);
+ }
+ }
+ });
+ // Client 1 requests FrameLayout
+ sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+ mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+ mInstrumentation.waitForIdleSync();
+
+ // Verify client 1 gets FrameLayout
+ verify(mMockClientCallback1, times(1))
+ .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+ assertEquals(FRAME_LAYOUT_DESCRIPTION,
+ mFindInfoCaptor.getValue().getContentDescription());
+
+ // Verify client 1 has prefetched nodes
+ verify(mMockClientCallback1, times(1))
+ .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+ eq(mMockClient1InteractionId));
+
+ // Verify client 1's only prefetched node is TextView1
+ List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue();
+ assertEquals(1, prefetchedNodes.size());
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription());
+
+ // Verify client 2 gets TextView1
+ verify(mMockClientCallback2, times(1))
+ .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt());
+
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, mFindInfoCaptor.getValue().getContentDescription());
+
+ // The second request was removed from queue and prefetching for this client request never
+ // occurred as it was satisfied.
+ verify(mMockClientCallback2, never())
+ .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt());
+
+ }
+
+ @Test
+ public void testFindNodeById_withTwoDifferentPrefetchFlags_shouldNotSatisfyPendingRequest()
+ throws RemoteException {
+ mSendRequestForTextAndIncludeUnImportantViews = true;
+ mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+ final long nodeId = AccessibilityNodeInfo.makeNodeId(
+ mTextView1.getAccessibilityViewId(),
+ AccessibilityNodeProvider.HOST_VIEW_ID);
+
+ if (mSendRequestForTextAndIncludeUnImportantViews) {
+ mSendRequestForTextAndIncludeUnImportantViews = false;
+ // TextView1 is prefetched here for client 1. Now enqueue a request from a
+ // different client that holds different fetch flags for TextView1
+ sendNodeRequestToController(nodeId, mMockClientCallback2,
+ mMockClient2InteractionId,
+ FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
+ }
+ }
+ });
+
+ // Mockito does not make copies of objects when called. It holds references, so
+ // the captor would point to client 2's results after all requests are processed. Verify
+ // prefetched node immediately
+ doAnswer(invocation -> {
+ List<AccessibilityNodeInfo> prefetched = invocation.getArgument(0);
+ assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetched.get(0).getContentDescription());
+ return null;
+ }).when(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(anyList(),
+ eq(mMockClient1InteractionId));
+
+ // Client 1 requests FrameLayout
+ sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1,
+ mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS);
+
+ mInstrumentation.waitForIdleSync();
+
+ // Verify client 1 gets FrameLayout
+ verify(mMockClientCallback1, times(1))
+ .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(),
+ eq(mMockClient1InteractionId));
+
+ assertEquals(FRAME_LAYOUT_DESCRIPTION,
+ mFindInfoCaptor.getValue().getContentDescription());
+
+ // Verify client 1 has prefetched results. The only prefetched node is TextView1
+ // (from above doAnswer)
+ verify(mMockClientCallback1, times(1))
+ .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+ eq(mMockClient1InteractionId));
+
+ // Verify client 2 gets TextView1
+ verify(mMockClientCallback2, times(1))
+ .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(),
+ eq(mMockClient2InteractionId));
+ assertEquals(TEXT_VIEW_1_DESCRIPTION,
+ mFindInfoCaptor.getValue().getContentDescription());
+ // Verify client 2 has TextView2 as a prefetched node
+ verify(mMockClientCallback2, times(1))
+ .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(),
+ eq(mMockClient2InteractionId));
+ List<AccessibilityNodeInfo> prefetchedNode = mPrefetchInfoListCaptor.getValue();
+ assertEquals(1, prefetchedNode.size());
+ assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNode.get(0).getContentDescription());
+ }
+
+ private void sendNodeRequestToController(long requestedNodeId,
+ IAccessibilityInteractionConnectionCallback callback, int interactionId,
+ int prefetchFlags) {
+ final int processAndThreadId = callback == mMockClientCallback1
+ ? MOCK_CLIENT_1_THREAD_AND_PROCESS_ID
+ : MOCK_CLIENT_2_THREAD_AND_PROCESS_ID;
+
+ mAccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdClientThread(
+ requestedNodeId,
+ null, interactionId,
+ callback, prefetchFlags,
+ processAndThreadId,
+ processAndThreadId, null, null);
+
+ }
+
+ private class TestFrameLayout extends FrameLayout {
+
+ TestFrameLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getWindowVisibility() {
+ // We aren't attached to a window so let's pretend
+ return VISIBLE;
+ }
+
+ @Override
+ public boolean isShown() {
+ // Controller check
+ return true;
+ }
+
+ @Override
+ public int getAccessibilityViewId() {
+ // static id doesn't reset after tests so return the same one
+ return 0;
+ }
+
+ @Override
+ public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+ // ViewGroup#addChildrenForAccessbility sorting logic will switch these two
+ outChildren.add(mTextView1);
+ outChildren.add(mTextView2);
+ }
+
+ @Override
+ public boolean includeForAccessibility() {
+ return true;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setContentDescription(FRAME_LAYOUT_DESCRIPTION);
+ }
+ }
+
+ private class TestTextView extends TextView {
+ TestTextView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getWindowVisibility() {
+ return VISIBLE;
+ }
+
+ @Override
+ public boolean isShown() {
+ return true;
+ }
+
+ @Override
+ public int getAccessibilityViewId() {
+ return 1;
+ }
+
+ @Override
+ public boolean includeForAccessibility() {
+ return true;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setContentDescription(TEXT_VIEW_1_DESCRIPTION);
+ }
+ }
+
+ private class TestTextView2 extends TextView {
+ TestTextView2(Context context) {
+ super(context);
+ }
+
+ @Override
+ public int getWindowVisibility() {
+ return VISIBLE;
+ }
+
+ @Override
+ public boolean isShown() {
+ return true;
+ }
+
+ @Override
+ public int getAccessibilityViewId() {
+ return 2;
+ }
+
+ @Override
+ public boolean includeForAccessibility() {
+ return true;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setContentDescription(TEXT_VIEW_2_DESCRIPTION);
+ }
+ }
+}
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/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 85b8fcbbcc61..ebb73e877db4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -39,6 +39,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.view.Display;
import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
@@ -172,8 +173,8 @@ public class KeyEventDispatcherTest {
mFilter1SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -204,8 +205,8 @@ public class KeyEventDispatcherTest {
mFilter2SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -221,8 +222,8 @@ public class KeyEventDispatcherTest {
mFilter2SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -238,8 +239,8 @@ public class KeyEventDispatcherTest {
mFilter2SequenceCaptor.getValue());
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
assertFalse(isTimeoutPending(mMessageCapturingHandler));
}
@@ -308,8 +309,8 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
}
@Test
@@ -357,8 +358,8 @@ public class KeyEventDispatcherTest {
mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
- verify(mMockPowerManagerService, times(1)).userActivity(anyLong(),
- eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
+ verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY),
+ anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0));
}
/*
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/accessibility/magnification/MagnificationGesturesObserverTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
index 895fb1757504..5dbf837b08b2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
@@ -69,7 +69,7 @@ public class MagnificationGesturesObserverTest {
mContext = InstrumentationRegistry.getContext();
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mObserver = new MagnificationGesturesObserver(mCallback, new SimpleSwipe(mContext),
- new TwoFingersDown(mContext));
+ new TwoFingersDownOrSwipe(mContext));
}
@Test
@@ -77,9 +77,7 @@ public class MagnificationGesturesObserverTest {
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X , DEFAULT_Y);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(moveEvent, moveEvent, 0);
- });
+ mObserver.onMotionEvent(moveEvent, moveEvent, 0);
verify(mCallback).onGestureCancelled(eq(0L),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(moveEvent)));
@@ -92,9 +90,7 @@ public class MagnificationGesturesObserverTest {
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X , DEFAULT_Y);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
verify(mCallback).onGestureCancelled(eq(0L),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(downEvent)));
@@ -108,9 +104,7 @@ public class MagnificationGesturesObserverTest {
final int timeoutMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
mContext) + 100;
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
verify(mCallback, timeout(timeoutMillis)).onGestureCancelled(eq(downEvent.getDownTime()),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(downEvent)));
@@ -121,14 +115,12 @@ public class MagnificationGesturesObserverTest {
public void sendEventsOfSwiping_onGestureCompleted() {
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X, DEFAULT_Y);
- final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X + swipeDistance, DEFAULT_Y + swipeDistance);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- mObserver.onMotionEvent(moveEvent, moveEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
+ mObserver.onMotionEvent(moveEvent, moveEvent, 0);
verify(mCallback).onGestureCompleted(eq(MagnificationGestureMatcher.GESTURE_SWIPE),
eq(downEvent.getDownTime()), mEventInfoArgumentCaptor.capture(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
index 01631bf21a63..0ca631e4ed62 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
@@ -78,7 +78,7 @@ public class SimpleSwipeTest {
@Test
public void sendSwipeEvent_onGestureCompleted() {
- final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X, DEFAULT_Y);
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
index ed8dc4e470de..162d2a9d98af 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -16,14 +16,20 @@
package com.android.server.accessibility.magnification;
+import static com.android.server.accessibility.utils.TouchEventGenerator.movePointer;
+import static com.android.server.accessibility.utils.TouchEventGenerator.twoPointersDownEvents;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.graphics.PointF;
import android.view.Display;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
@@ -35,18 +41,20 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
- * Tests for {@link TwoFingersDown}.
+ * Tests for {@link TwoFingersDownOrSwipe}.
*/
-public class TwoFingersDownTest {
+public class TwoFingersDownOrSwipeTest {
private static final float DEFAULT_X = 100f;
private static final float DEFAULT_Y = 100f;
- private static Context sContext;
+ private static float sSwipeMinDistance;
private static int sTimeoutMillis;
+ private static Context sContext;
- private Context mContext;
private GesturesObserver mGesturesObserver;
@Mock
private GesturesObserver.Listener mListener;
@@ -56,13 +64,13 @@ public class TwoFingersDownTest {
sContext = InstrumentationRegistry.getContext();
sTimeoutMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
sContext) + 100;
+ sSwipeMinDistance = ViewConfiguration.get(sContext).getScaledTouchSlop() + 1;
}
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
- mGesturesObserver = new GesturesObserver(mListener, new TwoFingersDown(mContext));
+ mGesturesObserver = new GesturesObserver(mListener, new TwoFingersDownOrSwipe(sContext));
}
@Test
@@ -78,24 +86,16 @@ public class TwoFingersDownTest {
@Test
public void sendTwoFingerDownEvent_onGestureCompleted() {
- final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
- DEFAULT_X, DEFAULT_Y);
- final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
- defPointerCoords.x = DEFAULT_X;
- defPointerCoords.y = DEFAULT_Y;
- final MotionEvent.PointerCoords secondPointerCoords = new MotionEvent.PointerCoords();
- secondPointerCoords.x = DEFAULT_X + 10;
- secondPointerCoords.y = DEFAULT_Y + 10;
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
- final MotionEvent twoPointersDownEvent = TouchEventGenerator.twoPointersDownEvent(
- Display.DEFAULT_DISPLAY, defPointerCoords, secondPointerCoords);
-
- mGesturesObserver.onMotionEvent(downEvent, downEvent, 0);
- mGesturesObserver.onMotionEvent(twoPointersDownEvent, twoPointersDownEvent, 0);
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted(
- MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, twoPointersDownEvent,
- twoPointersDownEvent, 0);
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, downEvents.get(1),
+ downEvents.get(1), 0);
}
@Test
@@ -108,7 +108,39 @@ public class TwoFingersDownTest {
mGesturesObserver.onMotionEvent(downEvent, downEvent, 0);
mGesturesObserver.onMotionEvent(upEvent, upEvent, 0);
- verify(mListener, timeout(sTimeoutMillis)).onGestureCancelled(any(MotionEvent.class),
- any(MotionEvent.class), eq(0));
+ verify(mListener, after(ViewConfiguration.getDoubleTapTimeout())).onGestureCancelled(
+ any(MotionEvent.class), any(MotionEvent.class), eq(0));
+ }
+
+ @Test
+ public void firstPointerMove_twoPointersDown_onGestureCompleted() {
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
+ final MotionEvent moveEvent = movePointer(downEvents.get(1), 0, sSwipeMinDistance, 0);
+
+ mGesturesObserver.onMotionEvent(moveEvent, moveEvent, 0);
+
+ verify(mListener).onGestureCompleted(
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, moveEvent,
+ moveEvent, 0);
+ }
+
+ @Test
+ public void secondPointerMove_twoPointersDown_onGestureCompleted() {
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
+ final MotionEvent moveEvent = movePointer(downEvents.get(1), 1, sSwipeMinDistance, 0);
+
+ mGesturesObserver.onMotionEvent(moveEvent, moveEvent, 0);
+
+ verify(mListener).onGestureCompleted(
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, moveEvent,
+ moveEvent, 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 4b7ebbc29b46..b9498d641ed7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -24,13 +24,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.EventStreamTransformation;
@@ -43,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.function.IntConsumer;
/**
@@ -75,7 +78,7 @@ public class WindowMagnificationGestureHandlerTest {
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
mock(WindowMagnificationManager.Callback.class));
mMockConnection = new MockWindowMagnificationConnection();
@@ -100,8 +103,8 @@ public class WindowMagnificationGestureHandlerTest {
* Covers following paths to get to and back between each state and {@link #STATE_IDLE}.
* <p>
* <br> IDLE -> SHOW_MAGNIFIER [label="a11y\nbtn"]
- * <br> SHOW_MAGNIFIER -> TWO_FINGER_DOWN [label="2hold"]
- * <br> TWO_FINGER_DOWN -> SHOW_MAGNIFIER [label="release"]
+ * <br> SHOW_MAGNIFIER -> TWO_FINGERS_DOWN [label="2hold"]
+ * <br> TWO_FINGERS_DOWN -> SHOW_MAGNIFIER [label="release"]
* <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]
* <br> IDLE -> SHOW_MAGNIFIER_TRIPLE_TAP [label="3tap"]
* <br> SHOW_MAGNIFIER_TRIPLE_TAP -> IDLE [label="3tap"]
@@ -112,18 +115,16 @@ public class WindowMagnificationGestureHandlerTest {
*/
@Test
public void testEachState_isReachableAndRecoverable() {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- forEachState(state -> {
- goFromStateIdleTo(state);
- assertIn(state);
- returnToNormalFrom(state);
- try {
- assertIn(STATE_IDLE);
- } catch (AssertionError e) {
- throw new AssertionError("Failed while testing state " + stateToString(state),
- e);
- }
- });
+ forEachState(state -> {
+ goFromStateIdleTo(state);
+ assertIn(state);
+ returnToNormalFrom(state);
+ try {
+ assertIn(STATE_IDLE);
+ } catch (AssertionError e) {
+ throw new AssertionError("Failed while testing state " + stateToString(state),
+ e);
+ }
});
}
@@ -209,10 +210,19 @@ public class WindowMagnificationGestureHandlerTest {
case STATE_TWO_FINGERS_DOWN: {
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
final Rect frame = mMockConnection.getMirrorWindowFrame();
- send(downEvent(frame.centerX(), frame.centerY()));
- //Second finger is outside the window.
- send(twoPointerDownEvent(new float[]{frame.centerX(), frame.centerX() + 10},
- new float[]{frame.centerY(), frame.centerY() + 10}));
+ final PointF firstPointerDown = new PointF(frame.centerX(), frame.centerY());
+ // The second finger is outside the window.
+ final PointF secondPointerDown = new PointF(frame.right + 10,
+ frame.bottom + 10);
+ final List<MotionEvent> motionEvents =
+ TouchEventGenerator.twoPointersDownEvents(DISPLAY_0,
+ firstPointerDown, secondPointerDown);
+ for (MotionEvent downEvent: motionEvents) {
+ send(downEvent);
+ }
+ // Wait for two-finger down gesture completed.
+ Thread.sleep(ViewConfiguration.getDoubleTapTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
@@ -301,16 +311,6 @@ public class WindowMagnificationGestureHandlerTest {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
- private MotionEvent twoPointerDownEvent(float[] x, float[] y) {
- final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
- defPointerCoords.x = x[0];
- defPointerCoords.y = y[0];
- final MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
- pointerCoords.x = x[1];
- pointerCoords.y = y[1];
- return TouchEventGenerator.twoPointersDownEvent(DISPLAY_0, defPointerCoords, pointerCoords);
- }
-
private String stateDump() {
return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
index a05881f78892..fbcde533aa9f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
@@ -19,16 +19,19 @@ package com.android.server.accessibility.utils;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.PointerCoords;
+import android.graphics.PointF;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* generates {@link MotionEvent} with source {@link InputDevice#SOURCE_TOUCHSCREEN}
- *
*/
public class TouchEventGenerator {
@@ -39,44 +42,68 @@ public class TouchEventGenerator {
public static MotionEvent moveEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_MOVE, x, y);
}
+
public static MotionEvent upEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_UP, x, y);
}
- public static MotionEvent twoPointersDownEvent(int displayId, PointerCoords defPointerCoords,
- PointerCoords pointerCoords) {
- return generateTwoPointersEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords,
- pointerCoords);
- }
-
private static MotionEvent generateSingleTouchEvent(int displayId, int action, float x,
float y) {
- final long downTime = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(downTime, downTime,
- action, x, y, 0);
- ev.setDisplayId(displayId);
- ev.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- return ev;
+ return generateMultiplePointersEvent(displayId, action, new PointF(x, y));
}
- private static MotionEvent generateTwoPointersEvent(int displayId, int action,
- PointerCoords defPointerCoords, PointerCoords pointerCoords) {
- final long downTime = SystemClock.uptimeMillis();
- MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties();
- defPointerProperties.id = 0;
- defPointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
- MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
- pointerProperties.id = 1;
- pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ /**
+ * Creates a list of {@link MotionEvent} with given pointers location.
+ *
+ * @param displayId the display id
+ * @param pointF1 location on the screen of the second pointer.
+ * @param pointF2 location on the screen of the second pointer.
+ * @return a list of {@link MotionEvent} with {@link MotionEvent#ACTION_DOWN} and {@link
+ * MotionEvent#ACTION_POINTER_DOWN}.
+ */
+ public static List<MotionEvent> twoPointersDownEvents(int displayId, PointF pointF1,
+ PointF pointF2) {
+ final List<MotionEvent> downEvents = new ArrayList<>();
+ final MotionEvent downEvent = generateMultiplePointersEvent(displayId,
+ MotionEvent.ACTION_DOWN, pointF1);
+ downEvents.add(downEvent);
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_DOWN | actionIndex;
+
+ final MotionEvent twoPointersDownEvent = generateMultiplePointersEvent(displayId, action,
+ pointF1, pointF2);
+ downEvents.add(twoPointersDownEvent);
+ return downEvents;
+ }
+
+ private static MotionEvent generateMultiplePointersEvent(int displayId, int action,
+ PointF... pointFs) {
+ final int length = pointFs.length;
+ final MotionEvent.PointerCoords[] pointerCoordsArray =
+ new MotionEvent.PointerCoords[length];
+ final MotionEvent.PointerProperties[] pointerPropertiesArray =
+ new MotionEvent.PointerProperties[length];
+ for (int i = 0; i < length; i++) {
+ MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
+ pointerCoords.x = pointFs[i].x;
+ pointerCoords.y = pointFs[i].y;
+ pointerCoordsArray[i] = pointerCoords;
+
+ MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
+ pointerProperties.id = i;
+ pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ pointerPropertiesArray[i] = pointerProperties;
+ }
+
+ final long downTime = SystemClock.uptimeMillis();
final MotionEvent ev = MotionEvent.obtain(
/* downTime */ downTime,
/* eventTime */ downTime,
/* action */ action,
- /* pointerCount */ 2,
- /* pointerProperties */ new MotionEvent.PointerProperties[] {
- defPointerProperties, pointerProperties},
- /* pointerCoords */ new PointerCoords[] { defPointerCoords, pointerCoords },
+ /* pointerCount */ length,
+ /* pointerProperties */ pointerPropertiesArray,
+ /* pointerCoords */ pointerCoordsArray,
/* metaState */ 0,
/* buttonState */ 0,
/* xPrecision */ 1.0f,
@@ -88,4 +115,65 @@ public class TouchEventGenerator {
ev.setDisplayId(displayId);
return ev;
}
+
+ /**
+ * Generates a move event that moves the pointer of the original event with given index.
+ * The original event should not be up event and we don't support
+ * {@link MotionEvent#ACTION_POINTER_UP} now.
+ *
+ * @param originalEvent the move or down event
+ * @param pointerIndex the index of the pointer we want to move.
+ * @param offsetX the offset in X coordinate.
+ * @param offsetY the offset in Y coordinate.
+ * @return a motion event with move action.
+ */
+ public static MotionEvent movePointer(MotionEvent originalEvent, int pointerIndex,
+ float offsetX, float offsetY) {
+ if (originalEvent.getActionMasked() == ACTION_UP) {
+ throw new IllegalArgumentException("No pointer is on the screen");
+ }
+
+ if (originalEvent.getActionMasked() == ACTION_POINTER_UP) {
+ throw new IllegalArgumentException("unsupported yet,please implement it first");
+ }
+
+ final int pointerCount = originalEvent.getPointerCount();
+ if (pointerIndex >= pointerCount) {
+ throw new IllegalArgumentException(
+ pointerIndex + "is not available with pointer count" + pointerCount);
+ }
+ final int action = MotionEvent.ACTION_MOVE;
+ final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+ originalEvent.getPointerProperties(i, pointerProperty);
+ pp[i] = pointerProperty;
+ }
+
+ final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+ originalEvent.getPointerCoords(i, pointerCoord);
+ pc[i] = pointerCoord;
+ }
+ pc[pointerIndex].x += offsetX;
+ pc[pointerIndex].y += offsetY;
+ final MotionEvent ev = MotionEvent.obtain(
+ /* downTime */ originalEvent.getDownTime(),
+ /* eventTime */ SystemClock.uptimeMillis(),
+ /* action */ action,
+ /* pointerCount */ 2,
+ /* pointerProperties */ pp,
+ /* pointerCoords */ pc,
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 1.0f,
+ /* yPrecision */ 1.0f,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ originalEvent.getSource(),
+ /* flags */ originalEvent.getFlags());
+ ev.setDisplayId(originalEvent.getDisplayId());
+ return ev;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 97daea3011ea..0b1c120d8a1e 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -67,7 +67,6 @@ public class SnippetTest {
.setPropertyName(propertyKeyString)
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setValuesIndex(0)
.setExactMatchPosition(29)
.setExactMatchBytes(3)
.setWindowPosition(26)
@@ -174,7 +173,6 @@ public class SnippetTest {
.setPropertyName("sender.name")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(4)
.setWindowPosition(0)
@@ -186,7 +184,6 @@ public class SnippetTest {
.setPropertyName("sender.email")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
- .setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(20)
.setWindowPosition(0)
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8b0e948579fb..b6b6932c4a93 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -602,12 +602,12 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L)
.build();
compatConfig.forceNonDebuggableFinalForTest(true);
- compatConfig.initOverrides(overridesFile);
+ compatConfig.initOverrides(overridesFile, new File(""));
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
.thenReturn(ApplicationInfoBuilder.create()
- .withPackageName("foo.bar")
- .debuggable()
- .build());
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build());
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
.thenThrow(new NameNotFoundException());
@@ -649,7 +649,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L)
.build();
compatConfig.forceNonDebuggableFinalForTest(true);
- compatConfig.initOverrides(overridesFile);
+ compatConfig.initOverrides(overridesFile, new File(""));
compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
new PackageOverride.Builder()
@@ -673,11 +673,11 @@ public class CompatConfigTest {
}
@Test
- public void testLoadOverridesRaw() throws Exception {
+ public void testInitOverridesRaw() throws Exception {
File tempDir = createTempDir();
File overridesFile = new File(tempDir, "overrides.xml");
// Change 1 is enabled for foo.bar (validated)
- // Change 2 is disabled for bar.baz (deferred)
+ // Change 2 is disabled for bar.baz (raw)
String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<overrides>\n"
+ " <change-overrides changeId=\"1\">\n"
@@ -709,7 +709,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L)
.build();
compatConfig.forceNonDebuggableFinalForTest(true);
- compatConfig.initOverrides(overridesFile);
+ compatConfig.initOverrides(overridesFile, new File(""));
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("foo.bar")
.withVersionCode(100L)
@@ -728,7 +728,7 @@ public class CompatConfigTest {
}
@Test
- public void testLoadOverridesDeferred() throws Exception {
+ public void testInitOverridesDeferred() throws Exception {
File tempDir = createTempDir();
File overridesFile = new File(tempDir, "overrides.xml");
// Change 1 is enabled for foo.bar (validated)
@@ -754,7 +754,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L)
.build();
compatConfig.forceNonDebuggableFinalForTest(true);
- compatConfig.initOverrides(overridesFile);
+ compatConfig.initOverrides(overridesFile, new File(""));
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("foo.bar")
.debuggable()
@@ -767,4 +767,115 @@ public class CompatConfigTest {
assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
}
+
+ @Test
+ public void testInitOverridesWithStaticFile() throws Exception {
+ File tempDir = createTempDir();
+ File dynamicOverridesFile = new File(tempDir, "dynamic_overrides.xml");
+ File staticOverridesFile = new File(tempDir, "static_overrides.xml");
+ // Change 1 is enabled for foo.bar (raw)
+ // Change 2 is disabled for bar.baz (raw)
+ String dynamicXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"1\">"
+ + "<raw>"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + "</raw>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<raw>"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ + " </raw-override-value>\n"
+ + "</raw>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "dynamic_overrides.xml", dynamicXmlData);
+ // Change 2 is enabled for foo.bar and bar.baz (raw)
+ // Change 3 is enabled for bar.baz (raw)
+ String staticXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<raw>"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + "</raw>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"3\">"
+ + "<raw>"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + "</raw>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "static_overrides.xml", staticXmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addDisabledChangeWithId(2L)
+ .addDisabledChangeWithId(3L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ // Adding an override that will be cleared after initOverrides is called.
+ compatConfig.addOverride(1L, "bar.baz", true);
+ compatConfig.initOverrides(dynamicOverridesFile, staticOverridesFile);
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.willChangeBeEnabled(1L, "foo.bar")).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "foo.bar")).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+ assertThat(compatConfig.willChangeBeEnabled(3L, "bar.baz")).isTrue();
+ assertThat(readFile(dynamicOverridesFile))
+ .isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"foo.bar\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"3\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <raw>\n"
+ + " <raw-override-value packageName=\"bar.baz\" "
+ + "minVersionCode=\"-9223372036854775808\" "
+ + "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ + " </raw-override-value>\n"
+ + " </raw>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 26a549d77664..a97ea268b1c8 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -274,6 +274,87 @@ public final class DeviceStateManagerServiceTest {
}
@Test
+ public void requestState_pendingStateAtRequest() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ mPolicy.blockConfigure();
+
+ final IBinder firstRequestToken = new Binder();
+ final IBinder secondRequestToken = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+ assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(firstRequestToken,
+ OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mService.getBinderService().requestState(secondRequestToken,
+ DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+
+ mPolicy.resumeConfigureOnce();
+
+ // First request status is now suspended as there is another pending request.
+ assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
+ TestDeviceStateManagerCallback.STATUS_SUSPENDED);
+ // Second request status still unknown because the service is still awaiting policy
+ // callback.
+ assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+
+ mPolicy.resumeConfigure();
+
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+
+ // Now cancel the second request to make the first request active.
+ mService.getBinderService().cancelRequest(secondRequestToken);
+
+ assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_sameAsBaseState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ }
+
+ @Test
public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
@@ -407,6 +488,14 @@ public final class DeviceStateManagerServiceTest {
}
}
+ public void resumeConfigureOnce() {
+ if (mPendingConfigureCompleteRunnable != null) {
+ Runnable onComplete = mPendingConfigureCompleteRunnable;
+ mPendingConfigureCompleteRunnable = null;
+ onComplete.run();
+ }
+ }
+
public int getMostRecentRequestedStateToConfigure() {
return mLastDeviceStateRequestedToConfigure;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 81b2381cd629..15ada896512b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -88,6 +88,7 @@ public class DisplayModeDirectorTest {
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
+ private static final int DISPLAY_ID = 0;
private Context mContext;
private FakesInjector mInjector;
@@ -107,19 +108,29 @@ public class DisplayModeDirectorTest {
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId) {
+ return createDirectorFromRefreshRateArray(refreshRates, baseModeId, refreshRates[0]);
+ }
+
+ private DisplayModeDirector createDirectorFromRefreshRateArray(
+ float[] refreshRates, int baseModeId, float defaultRefreshRate) {
DisplayModeDirector director =
new DisplayModeDirector(mContext, mHandler, mInjector);
- int displayId = 0;
Display.Mode[] modes = new Display.Mode[refreshRates.length];
+ Display.Mode defaultMode = null;
for (int i = 0; i < refreshRates.length; i++) {
modes[i] = new Display.Mode(
/*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
+ if (refreshRates[i] == defaultRefreshRate) {
+ defaultMode = modes[i];
+ }
}
+ assertThat(defaultMode).isNotNull();
+
SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
- supportedModesByDisplay.put(displayId, modes);
+ supportedModesByDisplay.put(DISPLAY_ID, modes);
director.injectSupportedModesByDisplay(supportedModesByDisplay);
SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
- defaultModesByDisplay.put(displayId, modes[0]);
+ defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
director.injectDefaultModeByDisplay(defaultModesByDisplay);
return director;
}
@@ -130,16 +141,15 @@ public class DisplayModeDirectorTest {
for (int i = 0; i < numRefreshRates; i++) {
refreshRates[i] = minFps + i;
}
- return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps);
+ return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps,
+ /*defaultRefreshRate=*/minFps);
}
@Test
public void testDisplayModeVoting() {
- int displayId = 0;
-
// With no votes present, DisplayModeDirector should allow any refresh rate.
DesiredDisplayModeSpecs modeSpecs =
- createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId);
+ createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(modeSpecs.baseModeId).isEqualTo(60);
assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
@@ -156,12 +166,12 @@ public class DisplayModeDirectorTest {
assertTrue(2 * numPriorities < maxFps - minFps + 1);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
for (int i = 0; i < numPriorities; i++) {
int priority = Vote.MIN_PRIORITY + i;
votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i));
director.injectVotesByDisplay(votesByDisplay);
- modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i);
assertThat(modeSpecs.primaryRefreshRateRange.min)
.isEqualTo((float) (minFps + i));
@@ -177,11 +187,11 @@ public class DisplayModeDirectorTest {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85));
votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80));
director.injectVotesByDisplay(votesByDisplay);
- modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(modeSpecs.baseModeId).isEqualTo(70);
assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f);
assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f);
@@ -190,18 +200,17 @@ public class DisplayModeDirectorTest {
@Test
public void testVotingWithFloatingPointErrors() {
- int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
float error = FLOAT_TOLERANCE / 4;
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error));
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE,
Vote.forRefreshRates(60 - error, 60 - error));
director.injectVotesByDisplay(votesByDisplay);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -213,15 +222,14 @@ public class DisplayModeDirectorTest {
assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
- int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -229,7 +237,7 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -237,7 +245,7 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -245,7 +253,7 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
}
@@ -261,14 +269,13 @@ public class DisplayModeDirectorTest {
assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
@@ -277,7 +284,7 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
@@ -285,7 +292,7 @@ public class DisplayModeDirectorTest {
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75));
director.injectVotesByDisplay(votesByDisplay);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.appRequestRefreshRateRange.min)
@@ -355,11 +362,10 @@ public class DisplayModeDirectorTest {
@Test
public void testVotingWithAlwaysRespectAppRequest() {
- final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60));
votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90));
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
@@ -369,7 +375,7 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -377,7 +383,7 @@ public class DisplayModeDirectorTest {
director.setShouldAlwaysRespectAppRequestedMode(true);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.baseModeId).isEqualTo(90);
@@ -385,7 +391,7 @@ public class DisplayModeDirectorTest {
director.setShouldAlwaysRespectAppRequestedMode(false);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
@@ -393,11 +399,10 @@ public class DisplayModeDirectorTest {
@Test
public void testVotingWithSwitchingTypeNone() {
- final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
- votesByDisplay.put(displayId, votes);
+ votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(30, 90));
votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
@@ -405,7 +410,7 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
.isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -417,7 +422,7 @@ public class DisplayModeDirectorTest {
assertThat(director.getModeSwitchingType())
.isEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
- desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
@@ -427,29 +432,38 @@ public class DisplayModeDirectorTest {
@Test
public void testVotingWithSwitchingTypeWithinGroups() {
- final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
assertThat(director.getModeSwitchingType())
.isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.allowGroupSwitching).isFalse();
}
@Test
public void testVotingWithSwitchingTypeWithinAndAcrossGroups() {
- final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
assertThat(director.getModeSwitchingType())
.isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
- DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.allowGroupSwitching).isTrue();
}
@Test
+ public void testDefaultDisplayModeIsSelectedIfAvailable() {
+ final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
+ final int defaultModeId = 3;
+ DisplayModeDirector director = createDirectorFromRefreshRateArray(
+ refreshRates, /*baseModeId=*/0, refreshRates[defaultModeId]);
+
+ DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(specs.baseModeId).isEqualTo(defaultModeId);
+ }
+
+ @Test
public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
new file mode 100644
index 000000000000..bcd853c76a79
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -0,0 +1,327 @@
+/*
+ * 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.display;
+
+import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED;
+import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED;
+import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+
+import android.app.PropertyInvalidatedCache;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LogicalDisplayMapperTest {
+ private static int sUniqueTestDisplayId = 0;
+
+ private DisplayDeviceRepository mDisplayDeviceRepo;
+ private LogicalDisplayMapper mLogicalDisplayMapper;
+ private Context mContext;
+
+ @Mock LogicalDisplayMapper.Listener mListenerMock;
+
+ @Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
+
+ @Before
+ public void setUp() {
+ // Share classloader to allow package private access.
+ System.setProperty("dexmaker.share_classloader", "true");
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getContext();
+ mDisplayDeviceRepo = new DisplayDeviceRepository(
+ new DisplayManagerService.SyncRoot(),
+ new PersistentDataStore(new PersistentDataStore.Injector() {
+ @Override
+ public InputStream openRead() {
+ return null;
+ }
+
+ @Override
+ public OutputStream startWrite() {
+ return null;
+ }
+
+ @Override
+ public void finishWrite(OutputStream os, boolean success) {}
+ }));
+
+ // Disable binder caches in this process.
+ PropertyInvalidatedCache.disableForTestMode();
+
+ mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo, mListenerMock);
+ }
+
+
+ /////////////////
+ // Test Methods
+ /////////////////
+
+ @Test
+ public void testDisplayDeviceAddAndRemove_Internal() {
+ DisplayDevice device = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
+
+ // add
+ LogicalDisplay displayAdded = add(device);
+ assertEquals(info(displayAdded).address, info(device).address);
+ assertEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+
+ // remove
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ verify(mListenerMock).onLogicalDisplayEventLocked(
+ mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
+ LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
+ assertEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+ assertEquals(displayAdded, displayRemoved);
+ }
+
+ @Test
+ public void testDisplayDeviceAddAndRemove_NonInternalTypes() {
+ testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_EXTERNAL);
+ testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_WIFI);
+ testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_OVERLAY);
+ testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_VIRTUAL);
+ testDisplayDeviceAddAndRemove_NonInternal(Display.TYPE_UNKNOWN);
+
+ // Call the internal test again, just to verify that adding non-internal displays
+ // doesn't affect the ability for an internal display to become the default display.
+ testDisplayDeviceAddAndRemove_Internal();
+ }
+
+ @Test
+ public void testDisplayDeviceAdd_TwoInternalOneDefault() {
+ DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0);
+ DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
+
+ LogicalDisplay display1 = add(device1);
+ assertEquals(info(display1).address, info(device1).address);
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(display1));
+
+ LogicalDisplay display2 = add(device2);
+ assertEquals(info(display2).address, info(device2).address);
+ assertEquals(Display.DEFAULT_DISPLAY, id(display2));
+ }
+
+ @Test
+ public void testDisplayDeviceAdd_TwoInternalBothDefault() {
+ DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
+ DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY);
+
+ LogicalDisplay display1 = add(device1);
+ assertEquals(info(display1).address, info(device1).address);
+ assertEquals(Display.DEFAULT_DISPLAY, id(display1));
+
+ LogicalDisplay display2 = add(device2);
+ assertEquals(info(display2).address, info(device2).address);
+ // Despite the flags, we can only have one default display
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(display2));
+ }
+
+ @Test
+ public void testGetDisplayIdsLocked() {
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0));
+ add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
+
+ int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
+ assertEquals(3, ids.length);
+ Arrays.sort(ids);
+ assertEquals(Display.DEFAULT_DISPLAY, ids[0]);
+ }
+
+ @Test
+ public void testSingleDisplayGroup() {
+ LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
+ LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
+
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
+ }
+
+ @Test
+ public void testMultipleDisplayGroups() {
+ LogicalDisplay display1 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
+
+
+ TestDisplayDevice device3 = createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800,
+ DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+ LogicalDisplay display3 = add(device3);
+
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
+ assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
+
+ // Now switch it back to the default group by removing the flag and issuing an update
+ DisplayDeviceInfo info = device3.getSourceInfo();
+ info.flags = info.flags & ~DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+
+ // Verify the new group is correct.
+ assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
+ }
+
+
+ /////////////////
+ // Helper Methods
+ /////////////////
+
+ private TestDisplayDevice createDisplayDevice(int type, int width, int height, int flags) {
+ return createDisplayDevice(new DisplayAddressImpl(), type, width, height, flags);
+ }
+
+ private TestDisplayDevice createDisplayDevice(
+ DisplayAddress address, int type, int width, int height, int flags) {
+ TestDisplayDevice device = new TestDisplayDevice();
+ DisplayDeviceInfo displayDeviceInfo = device.getSourceInfo();
+ displayDeviceInfo.type = type;
+ displayDeviceInfo.width = width;
+ displayDeviceInfo.height = height;
+ displayDeviceInfo.flags = flags;
+ displayDeviceInfo.address = new DisplayAddressImpl();
+ return device;
+ }
+
+ private DisplayDeviceInfo info(DisplayDevice device) {
+ return device.getDisplayDeviceInfoLocked();
+ }
+
+ private DisplayInfo info(LogicalDisplay display) {
+ return display.getDisplayInfoLocked();
+ }
+
+ private int id(LogicalDisplay display) {
+ return display.getDisplayIdLocked();
+ }
+
+ private LogicalDisplay add(DisplayDevice device) {
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_ADDED);
+ ArgumentCaptor<LogicalDisplay> displayCaptor =
+ ArgumentCaptor.forClass(LogicalDisplay.class);
+ verify(mListenerMock).onLogicalDisplayEventLocked(
+ displayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_ADDED));
+ clearInvocations(mListenerMock);
+ return displayCaptor.getValue();
+ }
+
+ private void testDisplayDeviceAddAndRemove_NonInternal(int type) {
+ DisplayDevice device = createDisplayDevice(type, 600, 800, 0);
+
+ // add
+ LogicalDisplay displayAdded = add(device);
+ assertEquals(info(displayAdded).address, info(device).address);
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+
+ // remove
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ verify(mListenerMock).onLogicalDisplayEventLocked(
+ mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
+ LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
+ assertNotEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+ }
+
+ /**
+ * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
+ * display-address implementation in our code. Intentionally uses default object (reference)
+ * equality rules.
+ */
+ class DisplayAddressImpl extends DisplayAddress {
+ @Override
+ public void writeToParcel(Parcel out, int flags) { }
+ }
+
+ class TestDisplayDevice extends DisplayDevice {
+ private DisplayDeviceInfo mInfo = new DisplayDeviceInfo();
+ private DisplayDeviceInfo mSentInfo;
+
+ TestDisplayDevice() {
+ super(null, null, "test_display_" + sUniqueTestDisplayId++, mContext);
+ mInfo = new DisplayDeviceInfo();
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mSentInfo == null) {
+ mSentInfo = new DisplayDeviceInfo();
+ mSentInfo.copyFrom(mInfo);
+ }
+ return mSentInfo;
+ }
+
+ @Override
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ mSentInfo = null;
+ }
+
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ public DisplayDeviceInfo getSourceInfo() {
+ return mInfo;
+ }
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
deleted file mode 100644
index 275e7c7fec04..000000000000
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/FontCrashDetectorTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.graphics.fonts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-@Presubmit
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class FontCrashDetectorTest {
-
- private File mCacheDir;
-
- @SuppressWarnings("ResultOfMethodCallIgnored")
- @Before
- public void setUp() {
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest");
- FileUtils.deleteContentsAndDir(mCacheDir);
- mCacheDir.mkdirs();
- }
-
- @Test
- public void detectCrash() throws Exception {
- // Prepare a marker file.
- File file = new File(mCacheDir, "detectCrash");
- assertThat(file.createNewFile()).isTrue();
-
- FontCrashDetector detector = new FontCrashDetector(file);
- assertThat(detector.hasCrashed()).isTrue();
-
- detector.clear();
- assertThat(detector.hasCrashed()).isFalse();
- assertThat(file.exists()).isFalse();
- }
-
- @Test
- public void monitorCrash() {
- File file = new File(mCacheDir, "monitorCrash");
- FontCrashDetector detector = new FontCrashDetector(file);
- assertThat(detector.hasCrashed()).isFalse();
-
- FontCrashDetector.MonitoredBlock block = detector.start();
- assertThat(file.exists()).isTrue();
-
- block.close();
- assertThat(file.exists()).isFalse();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index f2254a98a70e..c08857ca9bdc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -74,8 +74,6 @@ public class ActiveSourceActionTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
-
mHdmiControlService = new HdmiControlService(mContextSpy) {
@Override
AudioManager getAudioManager() {
@@ -106,15 +104,11 @@ public class ActiveSourceActionTest {
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 44418ce1e9c4..50ba761cef10 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -76,8 +76,6 @@ public class ArcInitiationActionFromAvrTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
-
HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
@@ -112,11 +110,6 @@ public class ArcInitiationActionFromAvrTest {
Looper getServiceLooper() {
return mTestLooper.getLooper();
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@@ -128,6 +121,7 @@ public class ArcInitiationActionFromAvrTest {
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
+ hdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index d454d8771e15..aa5bc933002d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -77,8 +77,6 @@ public class ArcTerminationActionFromAvrTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
-
HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
@@ -113,15 +111,11 @@ public class ArcTerminationActionFromAvrTest {
Looper getServiceLooper() {
return mTestLooper.getLooper();
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
+ hdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 7cb72c414e52..ef7b274eeb83 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -86,8 +86,6 @@ public class DevicePowerStatusActionTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
-
mHdmiControlService = new HdmiControlService(mContextSpy) {
@Override
AudioManager getAudioManager() {
@@ -118,15 +116,11 @@ public class DevicePowerStatusActionTest {
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index 9bf95c0edcdb..678f8b219e28 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -106,8 +106,6 @@ public class DeviceSelectActionTest {
PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mMyLooper));
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
-
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
@@ -133,16 +131,12 @@ public class DeviceSelectActionTest {
protected PowerManager getPowerManager() {
return powerManager;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
mHdmiCecLocalDeviceTv.init();
mHdmiControlService.setIoLooper(mMyLooper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index eedbc958dcd5..6bb148d43a57 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -98,8 +98,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mMyLooper));
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
-
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
@@ -188,17 +186,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
protected PowerManager getPowerManager() {
return powerManager;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.VOLUME_CONTROL_ENABLED);
mMyLooper = mTestLooper.getLooper();
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index b11ac24b9a29..915392e6eb80 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -87,7 +87,6 @@ public class HdmiCecLocalDevicePlaybackTest {
mMyLooper = mTestLooper.getLooper();
PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mMyLooper));
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -136,15 +135,11 @@ public class HdmiCecLocalDevicePlaybackTest {
protected PowerManager getPowerManager() {
return powerManager;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
mHdmiCecLocalDevicePlayback.init();
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 0717112da12c..b3f008598dc8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -128,8 +128,6 @@ public class HdmiCecLocalDeviceTest {
Context context = InstrumentationRegistry.getTargetContext();
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
-
mHdmiControlService =
new HdmiControlService(context) {
@Override
@@ -163,13 +161,9 @@ public class HdmiCecLocalDeviceTest {
void wakeUp() {
mWakeupMessageReceived = true;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 4623eb5b7d4b..4b3ef2f2cfd1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -81,8 +81,6 @@ public class HdmiCecLocalDeviceTvTest {
PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mMyLooper));
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
-
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@Override
@@ -119,16 +117,12 @@ public class HdmiCecLocalDeviceTvTest {
AudioManager getAudioManager() {
return mAudioManager;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
mHdmiCecLocalDeviceTv.init();
mHdmiControlService.setIoLooper(mMyLooper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 06373c2284b2..1c7ff421fe92 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -62,12 +62,12 @@ public class HdmiCecPowerStatusControllerTest {
private FakeNativeWrapper mNativeWrapper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
- private int mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
private IThermalService mIThermalServiceMock;
private HdmiControlService mHdmiControlService;
+ private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
@Before
public void setUp() throws Exception {
@@ -81,8 +81,6 @@ public class HdmiCecPowerStatusControllerTest {
when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(contextSpy);
-
mHdmiControlService = new HdmiControlService(contextSpy) {
@Override
boolean isControlEnabled() {
@@ -100,33 +98,24 @@ public class HdmiCecPowerStatusControllerTest {
}
@Override
- int getCecVersion() {
- return mHdmiCecVersion;
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
- HdmiCecLocalDevicePlayback hdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(
mHdmiControlService);
- hdmiCecLocalDevicePlayback.init();
+ mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(myLooper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(contextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mLocalDevices.add(hdmiCecLocalDevicePlayback);
+ mLocalDevices.add(mHdmiCecLocalDevicePlayback);
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
@@ -188,77 +177,84 @@ public class HdmiCecPowerStatusControllerTest {
@Test
public void setPowerStatus_doesntSendBroadcast_1_4() {
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@Test
public void setPowerStatus_transient_doesntSendBroadcast_1_4() {
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
mHdmiCecPowerStatusController.setPowerStatus(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@Test
public void setPowerStatus_fast_transient_doesntSendBroadcast_1_4() {
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
mHdmiCecPowerStatusController.setPowerStatus(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@Test
public void setPowerStatus_sendsBroadcast_2_0() {
- mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
-
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_ON);
assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
}
@Test
public void setPowerStatus_transient_sendsBroadcast_2_0() {
- mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
-
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
mHdmiCecPowerStatusController.setPowerStatus(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
}
@Test
public void setPowerStatus_fast_transient_doesntSendBroadcast_2_0() {
- mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
-
+ setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
mHdmiCecPowerStatusController.setPowerStatus(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
mTestLooper.dispatchAll();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST,
+ mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
+
+ private void setCecVersion(int version) {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, version);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 32a70480c39e..47f3bf9daba7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -19,6 +19,7 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -44,12 +45,12 @@ import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -68,14 +69,64 @@ import java.util.Optional;
@RunWith(JUnit4.class)
public class HdmiControlServiceTest {
- private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDeviceSource {
+ private class MockPlaybackDevice extends HdmiCecLocalDevicePlayback {
private boolean mCanGoToStandby;
private boolean mIsStandby;
private boolean mIsDisabled;
- protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
- super(service, deviceType);
+ MockPlaybackDevice(HdmiControlService service) {
+ super(service);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {}
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {}
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+ private class MockAudioSystemDevice extends HdmiCecLocalDeviceAudioSystem {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ MockAudioSystemDevice(HdmiControlService service) {
+ super(service);
}
@Override
@@ -123,8 +174,8 @@ public class HdmiControlServiceTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
- private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
- private HdmiCecLocalDeviceMyDevice mMyPlaybackDevice;
+ private MockAudioSystemDevice mAudioSystemDevice;
+ private MockPlaybackDevice mPlaybackDevice;
private FakeNativeWrapper mNativeWrapper;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@@ -144,6 +195,7 @@ public class HdmiControlServiceTest {
PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
mIThermalServiceMock, null);
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
@@ -157,21 +209,17 @@ public class HdmiControlServiceTest {
@Override
protected void writeStringSystemProperty(String key, String value) {
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
mMyLooper = mTestLooper.getLooper();
- mMyAudioSystemDevice =
- new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_AUDIO_SYSTEM);
- mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_PLAYBACK);
- mMyAudioSystemDevice.init();
- mMyPlaybackDevice.init();
+ mAudioSystemDevice = new MockAudioSystemDevice(mHdmiControlService);
+ mPlaybackDevice = new MockPlaybackDevice(mHdmiControlService);
+ mAudioSystemDevice.init();
+ mPlaybackDevice.init();
mHdmiControlService.setIoLooper(mMyLooper);
+ mHdmiControlService.setHdmiCecConfig(hdmiCecConfig);
+ mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
@@ -180,8 +228,8 @@ public class HdmiControlServiceTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mLocalDevices.add(mMyAudioSystemDevice);
- mLocalDevices.add(mMyPlaybackDevice);
+ mLocalDevices.add(mAudioSystemDevice);
+ mLocalDevices.add(mPlaybackDevice);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
@@ -192,6 +240,9 @@ public class HdmiControlServiceTest {
mHdmiPortInfo[3] =
new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -201,13 +252,13 @@ public class HdmiControlServiceTest {
@Test
public void onStandby_notByCec_cannotGoToStandby() {
mStandbyMessageReceived = false;
- mMyPlaybackDevice.setCanGoToStandby(false);
+ mPlaybackDevice.setCanGoToStandby(false);
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
- assertTrue(mMyPlaybackDevice.isStandby());
- assertTrue(mMyAudioSystemDevice.isStandby());
- assertFalse(mMyPlaybackDevice.isDisabled());
- assertFalse(mMyAudioSystemDevice.isDisabled());
+ assertTrue(mPlaybackDevice.isStandby());
+ assertTrue(mAudioSystemDevice.isStandby());
+ assertFalse(mPlaybackDevice.isDisabled());
+ assertFalse(mAudioSystemDevice.isDisabled());
}
@Test
@@ -215,10 +266,10 @@ public class HdmiControlServiceTest {
mStandbyMessageReceived = true;
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
- assertTrue(mMyPlaybackDevice.isStandby());
- assertTrue(mMyAudioSystemDevice.isStandby());
- assertTrue(mMyPlaybackDevice.isDisabled());
- assertTrue(mMyAudioSystemDevice.isDisabled());
+ assertTrue(mPlaybackDevice.isStandby());
+ assertTrue(mAudioSystemDevice.isStandby());
+ assertTrue(mPlaybackDevice.isDisabled());
+ assertTrue(mAudioSystemDevice.isDisabled());
}
@Test
@@ -275,6 +326,7 @@ public class HdmiControlServiceTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mTestLooper.dispatchAll();
mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
mNativeWrapper.clearResultMessages();
@@ -555,8 +607,8 @@ public class HdmiControlServiceTest {
HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
- mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
- mMyPlaybackDevice.getDeviceFeatures());
+ mPlaybackDevice.getRcProfile(), mPlaybackDevice.getRcFeatures(),
+ mPlaybackDevice.getDeviceFeatures());
assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures);
}
@@ -573,8 +625,8 @@ public class HdmiControlServiceTest {
HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
- mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
- mMyPlaybackDevice.getDeviceFeatures());
+ mPlaybackDevice.getRcProfile(), mPlaybackDevice.getRcFeatures(),
+ mPlaybackDevice.getDeviceFeatures());
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportFeatures);
}
@@ -590,8 +642,8 @@ public class HdmiControlServiceTest {
HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
- mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
- mMyPlaybackDevice.getDeviceFeatures());
+ mPlaybackDevice.getRcProfile(), mPlaybackDevice.getRcFeatures(),
+ mPlaybackDevice.getDeviceFeatures());
assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures);
}
@@ -612,41 +664,42 @@ public class HdmiControlServiceTest {
assertEquals(runnerUid, Binder.getCallingWorkSourceUid());
}
- @Ignore("b/180499471")
@Test
public void initCecVersion_limitToMinimumSupportedVersion() {
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
- mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- mHdmiControlService.initService();
+ mTestLooper.dispatchAll();
assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
}
- @Ignore("b/180499471")
@Test
public void initCecVersion_limitToAtLeast1_4() {
+ Log.e("MARVIN", "set HAL CEC to 0");
+ mNativeWrapper.setCecVersion(0x0);
+ Log.e("MARVIN", "set setting CEC to 2");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
- mNativeWrapper.setCecVersion(0x0);
- mHdmiControlService.initService();
+ mTestLooper.dispatchAll();
assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
}
- @Ignore("b/180499471")
@Test
public void initCecVersion_useHighestMatchingVersion() {
+ mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ Log.e("MARVIN", "set setting CEC");
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
- mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0);
- mHdmiControlService.initService();
+ mTestLooper.dispatchAll();
assertThat(mHdmiControlService.getCecVersion()).isEqualTo(
HdmiControlManager.HDMI_CEC_VERSION_2_0);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index b8dfd5672056..605f781b23df 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -84,8 +84,6 @@ public class PowerStatusMonitorActionTest {
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
-
mHdmiControlService = new HdmiControlService(mContextSpy) {
@Override
AudioManager getAudioManager() {
@@ -116,15 +114,11 @@ public class PowerStatusMonitorActionTest {
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
@@ -185,6 +179,7 @@ public class PowerStatusMonitorActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mTestLooper.dispatchAll();
sendMessageFromPlaybackDevice(ADDR_PLAYBACK_1, 0x1000);
reportPowerStatus(ADDR_PLAYBACK_1, true, HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index f9160abcbfbf..e82c788020ed 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -65,8 +65,6 @@ public class SystemAudioInitiationActionFromAvrTest {
Context context = InstrumentationRegistry.getTargetContext();
- HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
-
HdmiControlService hdmiControlService = new HdmiControlService(context) {
@Override
void sendCecCommand(
@@ -164,15 +162,11 @@ public class SystemAudioInitiationActionFromAvrTest {
int pathToPortId(int path) {
return -1;
}
-
- @Override
- protected HdmiCecConfig getHdmiCecConfig() {
- return hdmiCecConfig;
- }
};
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
+ hdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
HdmiCecController.NativeWrapper nativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
hdmiControlService, nativeWrapper, hdmiControlService.getAtomWriter());
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..dd9ae6592f5c 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -19,14 +19,18 @@ package com.android.server.job;
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_BGUSER_IMPORTANT;
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;
import android.annotation.NonNull;
+import android.util.Log;
import android.util.Pair;
import android.util.SparseIntArray;
@@ -50,11 +54,11 @@ import java.util.Random;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class WorkCountTrackerTest {
- private static final String TAG = "WorkerCountTrackerTest";
+ private static final String TAG = "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, 1.0 / NUM_WORK_TYPES);
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -66,12 +70,16 @@ 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 pBgUserImp, double pBgUser) {
+ return buildCdf(pTop, pFgs, pEj, pBg, pBgUserImp, pBgUser);
}
@NonNull
private static double[] buildCdf(double... probs) {
+ if (probs.length == 0) {
+ throw new IllegalArgumentException("Must supply at least one probability");
+ }
double[] cdf = new double[probs.length];
double sum = 0;
@@ -81,7 +89,9 @@ public class WorkCountTrackerTest {
}
if (Double.compare(1, sum) != 0) {
- throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+ Log.e(TAG, "probabilities don't sum to one: " + sum);
+ // 1.0/6 doesn't work well in code :/
+ cdf[cdf.length - 1] = 1;
}
return cdf;
}
@@ -102,10 +112,14 @@ 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_IMPORTANT;
+ case 5:
return WORK_TYPE_BGUSER;
default:
throw new IllegalStateException("Unknown work type");
@@ -224,12 +238,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 +321,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, 0);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -322,7 +339,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, 0, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.75, .2, .05);
final double probStart = 0.5;
@@ -340,7 +357,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, 0, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.05, .95);
final double probStart = 0.5;
@@ -358,7 +375,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, 0.02, .08);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -376,7 +393,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, 0);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -394,7 +411,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, 0.05, .75);
final double[] numTypesCdf = buildCdf(0.5, 0.5);
final double probStart = 0.5;
@@ -413,7 +430,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, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -432,7 +449,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, 0.5);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -451,7 +468,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, 0.9);
final double[] numTypesCdf = buildCdf(0.9, 0.1);
final double probStart = 0.5;
@@ -470,7 +487,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, 0.1);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -488,7 +505,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, 0);
final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
final double probStart = 0.5;
@@ -511,7 +528,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.7, 0.1, 0.05);
final double probStart = 0.87;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
@@ -528,7 +545,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, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -548,7 +565,28 @@ 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, 0.4);
+ final double[] numTypesCdf = buildCdf(0.7, 0.3);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ cdf, numTypesCdf, probStop);
+ }
+
+ @Test
+ public void testRandom16() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 7;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER_IMPORTANT, 1),
+ Pair.create(WORK_TYPE_BGUSER, 1));
+ 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.09, 0.25, 0.05, 0.3, 0.3);
final double[] numTypesCdf = buildCdf(0.7, 0.3);
final double probStart = 0.5;
@@ -576,11 +614,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);
}
}
@@ -738,6 +778,7 @@ public class WorkCountTrackerTest {
/* resPen */ List.of(
Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
+ Log.d(TAG, "START***#*#*#*#*#*#**#*");
// Test multi-types
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
@@ -754,7 +795,10 @@ public class WorkCountTrackerTest {
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2),
Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
/* resPen */ List.of(
- Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)));
+ // Not checking BG count because the test starts jobs in random order
+ // and if it tries to start 4 BG jobs (2 will run as EJ from EJ|BG), but
+ // the resulting pending will be 3 BG instead of 4 BG.
+ Pair.create(WORK_TYPE_BGUSER, 1)));
}
/** Tests that the counter updates properly when jobs are stopped. */
@@ -938,10 +982,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 +1024,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..a5fedef23e00 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -17,8 +17,11 @@ 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_BGUSER_IMPORTANT;
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,12 +47,16 @@ 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_IMPORTANT = "concurrency_max_bguser_important_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_IMPORTANT = "concurrency_min_bguser_important_test";
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@After
@@ -59,15 +66,21 @@ 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_IMPORTANT, 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_IMPORTANT, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
}
private void check(@Nullable DeviceConfig.Properties config,
@@ -103,10 +116,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 +208,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 +312,34 @@ 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_IMPORTANT, 12)
+ .setInt(KEY_MIN_BGUSER_IMPORTANT, 5)
+ .setInt(KEY_MAX_BGUSER, 11)
+ .setInt(KEY_MIN_BGUSER, 6)
+ .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_IMPORTANT, 5),
+ Pair.create(WORK_TYPE_BGUSER, 6)),
+ /* 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_IMPORTANT, 12),
+ Pair.create(WORK_TYPE_BGUSER, 11)));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index a896f1b0d60f..b51f4df43259 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -44,6 +44,7 @@ import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserManager;
@@ -62,6 +63,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import javax.crypto.SecretKey;
@@ -181,6 +183,18 @@ public class RebootEscrowManagerTests {
}
@Override
+ public int getLoadEscrowDataRetryLimit() {
+ // Try two times
+ return 2;
+ }
+
+ @Override
+ public int getLoadEscrowDataRetryIntervalSeconds() {
+ // Retry in 1 seconds
+ return 1;
+ }
+
+ @Override
public void reportMetric(boolean success) {
mInjected.reportMetric(success);
}
@@ -448,6 +462,46 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
+ setServerBasedRebootEscrowProvider();
+
+ when(mInjected.getBootCount()).thenReturn(0);
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ // Use x -> x for both wrap & unwrap functions.
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenThrow(new IOException())
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
+ thread.start();
+ mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
+ // Sleep 5s for the retry to complete
+ Thread.sleep(5 * 1000);
+ verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
+ assertTrue(metricsSuccessCaptor.getValue());
+ verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+ }
+
+ @Test
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
when(mInjected.getBootCount()).thenReturn(0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2d6605ae222f..c0a38b874914 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -239,8 +239,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
- mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
- .getResponseCode();
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
@@ -268,8 +268,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
- .getResponseCode();
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID);
@@ -294,8 +294,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
- .getResponseCode();
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
mService.setLockCredential(pattern, password, PRIMARY_USER_ID);
@@ -369,6 +369,36 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
@Test
+ public void testActivateMultipleEscrowTokens() throws Exception {
+ byte[] token0 = "some-high-entropy-secure-token-0".getBytes();
+ byte[] token1 = "some-high-entropy-secure-token-1".getBytes();
+ byte[] token2 = "some-high-entropy-secure-token-2".getBytes();
+
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential pattern = newPattern("123654");
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+
+ long handle0 = mLocalService.addEscrowToken(token0, PRIMARY_USER_ID, null);
+ long handle1 = mLocalService.addEscrowToken(token1, PRIMARY_USER_ID, null);
+ long handle2 = mLocalService.addEscrowToken(token2, PRIMARY_USER_ID, null);
+
+ // Activate token
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
+
+ // Verify tokens work
+ assertTrue(mLocalService.isEscrowTokenActive(handle0, PRIMARY_USER_ID));
+ assertTrue(mLocalService.setLockCredentialWithToken(
+ pattern, handle0, token0, PRIMARY_USER_ID));
+ assertTrue(mLocalService.isEscrowTokenActive(handle1, PRIMARY_USER_ID));
+ assertTrue(mLocalService.setLockCredentialWithToken(
+ pattern, handle1, token1, PRIMARY_USER_ID));
+ assertTrue(mLocalService.isEscrowTokenActive(handle2, PRIMARY_USER_ID));
+ assertTrue(mLocalService.setLockCredentialWithToken(
+ pattern, handle2, token2, PRIMARY_USER_ID));
+ }
+
+ @Test
public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
LockscreenCredential password = newPassword("password");
LockscreenCredential pattern = newPattern("123654");
@@ -494,8 +524,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
reset(mDevicePolicyManager);
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
- mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
- .getResponseCode();
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
mService.onCleanupUser(PRIMARY_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 74bf4f5da70d..13c3919cefc5 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2041,7 +2041,8 @@ public class NetworkPolicyManagerServiceTest {
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(TRANSPORT_WIFI);
networkCapabilities.setSSID(TEST_SSID);
- return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null);
+ return new NetworkState(TYPE_WIFI, prop, networkCapabilities, new Network(TEST_NET_ID),
+ null);
}
private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index ea27331ac4ca..6e5fbd0b6ed0 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -764,7 +764,7 @@ public class PowerManagerServiceTest {
createService();
startSystem();
- mService.getBinderServiceInstance().userActivity(mClock.now(),
+ mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
verify(mInattentiveSleepWarningControllerMock, never()).show();
@@ -773,7 +773,7 @@ public class PowerManagerServiceTest {
verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean());
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
- mService.getBinderServiceInstance().userActivity(mClock.now(),
+ mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 11fb0021be62..624c3de650aa 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -144,6 +144,9 @@ public class AppStandbyControllerTests {
private static final long RARE_THRESHOLD = 48 * HOUR_MS;
private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS;
+ private static final int ASSERT_RETRY_ATTEMPTS = 20;
+ private static final int ASSERT_RETRY_DELAY_MILLISECONDS = 500;
+
/** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
private static boolean isPackageInstalled = true;
@@ -589,16 +592,37 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime);
}
- private void assertBucket(int bucket) {
+ private void assertBucket(int bucket) throws InterruptedException {
assertBucket(bucket, PACKAGE_1);
}
- private void assertBucket(int bucket, String pkg) {
+ private void assertBucket(int bucket, String pkg) throws InterruptedException {
+ int retries = 0;
+ do {
+ if (bucket == getStandbyBucket(mController, pkg)) {
+ // Success
+ return;
+ }
+ Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS);
+ retries++;
+ } while(retries < ASSERT_RETRY_ATTEMPTS);
+ // try one last time
assertEquals(bucket, getStandbyBucket(mController, pkg));
}
- private void assertNotBucket(int bucket) {
- assertNotEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
+ private void assertNotBucket(int bucket) throws InterruptedException {
+ final String pkg = PACKAGE_1;
+ int retries = 0;
+ do {
+ if (bucket != getStandbyBucket(mController, pkg)) {
+ // Success
+ return;
+ }
+ Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS);
+ retries++;
+ } while(retries < ASSERT_RETRY_ATTEMPTS);
+ // try one last time
+ assertNotEquals(bucket, getStandbyBucket(mController, pkg));
}
@Test
@@ -996,7 +1020,7 @@ public class AppStandbyControllerTests {
* a low bucket after the RESTRICTED timeout.
*/
@Test
- public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() {
+ public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
@@ -1032,7 +1056,7 @@ public class AppStandbyControllerTests {
* a low bucket after the RESTRICTED timeout.
*/
@Test
- public void testRestrictedTimeoutOverridesPredictionLowBucket() {
+ public void testRestrictedTimeoutOverridesPredictionLowBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Not long enough to time out into RESTRICTED.
@@ -1055,7 +1079,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testRestrictedBucketDisabled() {
+ public void testRestrictedBucketDisabled() throws Exception {
mInjector.mIsRestrictedBucketEnabled = false;
// Get the controller to read the new value. Capturing the ContentObserver isn't possible
// at the moment.
@@ -1080,7 +1104,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testRestrictedBucket_EnabledToDisabled() {
+ public void testRestrictedBucket_EnabledToDisabled() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
@@ -1097,7 +1121,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
+ public void testPredictionRaiseFromRestrictedTimeout_highBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
@@ -1114,7 +1138,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testPredictionRaiseFromRestrictedTimeout_lowBucket() {
+ public void testPredictionRaiseFromRestrictedTimeout_lowBucket() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
@@ -1366,7 +1390,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testAddActiveDeviceAdmin() {
+ public void testAddActiveDeviceAdmin() throws Exception {
assertActiveAdmins(USER_ID, (String[]) null);
assertActiveAdmins(USER_ID2, (String[]) null);
@@ -1402,7 +1426,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void isActiveDeviceAdmin() {
+ public void isActiveDeviceAdmin() throws Exception {
assertActiveAdmins(USER_ID, (String[]) null);
assertActiveAdmins(USER_ID2, (String[]) null);
@@ -1488,7 +1512,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testAppUpdateOnRestrictedBucketStatus() {
+ public void testAppUpdateOnRestrictedBucketStatus() throws Exception {
// Updates shouldn't change bucket if the app timed out.
// Way past all timeouts. App times out into RESTRICTED bucket.
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
@@ -1563,7 +1587,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testSystemHeadlessAppElevated() {
+ public void testSystemHeadlessAppElevated() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime,
PACKAGE_SYSTEM_HEADFULL);
@@ -1589,7 +1613,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testWellbeingAppElevated() {
+ public void testWellbeingAppElevated() throws Exception {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING);
assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 4634e12f1530..2a3c2c46ce4e 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -100,16 +100,17 @@ final class FakeVibratorControllerProvider {
return EFFECT_DURATION;
}
- public void compose(VibrationEffect.Composition.PrimitiveEffect[] effect,
+ public long compose(VibrationEffect.Composition.PrimitiveEffect[] effect,
long vibrationId) {
VibrationEffect.Composed composed = new VibrationEffect.Composed(Arrays.asList(effect));
mEffects.add(composed);
applyLatency();
- long duration = EFFECT_DURATION * effect.length;
+ long duration = 0;
for (VibrationEffect.Composition.PrimitiveEffect e : effect) {
- duration += e.delay;
+ duration += EFFECT_DURATION + e.delay;
}
scheduleListener(duration, vibrationId);
+ return duration;
}
public void setExternalControl(boolean enabled) {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index 8c62b7fe235e..3ca90603e9d2 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -91,6 +91,7 @@ public class InputDeviceDelegateTest {
mInputDeviceDelegate = new InputDeviceDelegate(
mContextSpy, new Handler(mTestLooper.getLooper()));
+ mInputDeviceDelegate.onSystemReady();
}
@After
@@ -99,6 +100,24 @@ public class InputDeviceDelegateTest {
}
@Test
+ public void beforeSystemReady_ignoresAnyUpdate() throws Exception {
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
+ InputDeviceDelegate inputDeviceDelegate = new InputDeviceDelegate(
+ mContextSpy, new Handler(mTestLooper.getLooper()));
+
+ inputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
+ assertFalse(inputDeviceDelegate.isAvailable());
+
+ inputDeviceDelegate.onInputDeviceAdded(1);
+ assertFalse(inputDeviceDelegate.isAvailable());
+
+ updateInputDevices(new int[]{1});
+ assertFalse(inputDeviceDelegate.isAvailable());
+
+ verify(mIInputManagerMock, never()).getInputDevice(anyInt());
+ }
+
+ @Test
public void onInputDeviceAdded_withSettingsDisabled_ignoresNewDevice() throws Exception {
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 1e6ef9137686..b6c11fe62ff6 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -88,6 +88,7 @@ public class VibrationScalerTest {
mVibrationSettings = new VibrationSettings(
mContextSpy, new Handler(mTestLooper.getLooper()));
mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
+ mVibrationSettings.onSystemReady();
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index d8679876965c..855012459bd6 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -106,6 +106,7 @@ public class VibrationSettingsTest {
mAudioManager = mContextSpy.getSystemService(AudioManager.class);
mVibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
+ mVibrationSettings.onSystemReady();
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
@@ -162,6 +163,23 @@ public class VibrationSettingsTest {
}
@Test
+ public void shouldVibrateForRingerMode_beforeSystemReady_returnsFalseOnlyForRingtone() {
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setRingerMode(AudioManager.RINGER_MODE_MAX);
+ VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
+ new Handler(mTestLooper.getLooper()));
+
+ assertFalse(vibrationSettings.shouldVibrateForRingerMode(
+ VibrationAttributes.USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
+ VibrationAttributes.USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
+ VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ }
+
+ @Test
public void shouldVibrateForRingerMode_withoutRingtoneUsage_returnsTrue() {
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
@@ -303,6 +321,37 @@ public class VibrationSettingsTest {
}
@Test
+ public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
+ new Handler(mTestLooper.getLooper()));
+
+ assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
+ vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(
+ VibrationAttributes.USAGE_PHYSICAL_EMULATION));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ }
+
+ @Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 815aa8ee66ae..bad3e4c2ed92 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.vibrator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -202,7 +203,7 @@ public class VibratorControllerTest {
VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
- controller.on(effect, 11);
+ assertEquals(10L, controller.on(effect, 11));
assertTrue(controller.isVibrating());
verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK),
@@ -212,13 +213,14 @@ public class VibratorControllerTest {
@Test
public void on_withComposed_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ when(mNativeWrapperMock.compose(any(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
VibrationEffect.Composed effect = (VibrationEffect.Composed)
VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
.compose();
- controller.on(effect, 12);
+ assertEquals(15L, controller.on(effect, 12));
ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index ba0a472c80dd..a28d18fb74d3 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -176,8 +176,14 @@ public class VibratorManagerServiceTest {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
}
+ private VibratorManagerService createSystemReadyService() {
+ VibratorManagerService service = createService();
+ service.systemReady();
+ return service;
+ }
+
private VibratorManagerService createService() {
- VibratorManagerService service = new VibratorManagerService(
+ return new VibratorManagerService(
mContextSpy,
new VibratorManagerService.Injector() {
@Override
@@ -201,8 +207,6 @@ public class VibratorManagerServiceTest {
void addService(String name, IBinder service) {
}
});
- service.systemReady();
- return service;
}
@Test
@@ -215,21 +219,44 @@ public class VibratorManagerServiceTest {
}
@Test
+ public void createService_doNotCrashIfUsedBeforeSystemReady() {
+ mockVibrators(1, 2);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+ mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+ VibratorManagerService service = createService();
+
+ assertNotNull(service.getVibratorIds());
+ assertNotNull(service.getVibratorInfo(1));
+ assertFalse(service.isVibrating(1));
+
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+ service.cancelVibrate(service);
+
+ assertTrue(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+
+ IVibratorStateListener listener = mockVibratorStateListener();
+ assertTrue(service.registerVibratorStateListener(1, listener));
+ assertTrue(service.unregisterVibratorStateListener(1, listener));
+ }
+
+ @Test
public void getVibratorIds_withNullResultFromNative_returnsEmptyArray() {
when(mNativeWrapperMock.getVibratorIds()).thenReturn(null);
- assertArrayEquals(new int[0], createService().getVibratorIds());
+ assertArrayEquals(new int[0], createSystemReadyService().getVibratorIds());
}
@Test
public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
mockVibrators(2, 1);
- assertArrayEquals(new int[]{2, 1}, createService().getVibratorIds());
+ assertArrayEquals(new int[]{2, 1}, createSystemReadyService().getVibratorIds());
}
@Test
public void getVibratorInfo_withMissingVibratorId_returnsNull() {
mockVibrators(1);
- assertNull(createService().getVibratorInfo(2));
+ assertNull(createSystemReadyService().getVibratorInfo(2));
}
@Test
@@ -239,7 +266,7 @@ public class VibratorManagerServiceTest {
vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
- VibratorInfo info = createService().getVibratorInfo(1);
+ VibratorInfo info = createSystemReadyService().getVibratorInfo(1);
assertNotNull(info);
assertEquals(1, info.getId());
@@ -257,7 +284,7 @@ public class VibratorManagerServiceTest {
@Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
mockVibrators(1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
IVibratorStateListener listenerMock = mockVibratorStateListener();
service.registerVibratorStateListener(1, listenerMock);
@@ -278,7 +305,7 @@ public class VibratorManagerServiceTest {
@Test
public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
mockVibrators(1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
IVibratorStateListener listenerMock = mockVibratorStateListener();
service.registerVibratorStateListener(1, listenerMock);
@@ -303,7 +330,7 @@ public class VibratorManagerServiceTest {
@Test
public void registerVibratorStateListener_multipleVibratorsAreTriggered() throws Exception {
mockVibrators(0, 1, 2);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
IVibratorStateListener[] listeners = new IVibratorStateListener[3];
for (int i = 0; i < 3; i++) {
listeners[i] = mockVibratorStateListener();
@@ -330,7 +357,8 @@ public class VibratorManagerServiceTest {
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
- assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+ assertTrue(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
VibrationEffect.Prebaked expectedEffect = new VibrationEffect.Prebaked(
VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
@@ -353,7 +381,8 @@ public class VibratorManagerServiceTest {
.addVibrator(2, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
.addVibrator(3, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
.combine();
- assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+ assertTrue(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
VibrationEffect.Prebaked expectedClick = new VibrationEffect.Prebaked(
VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
@@ -376,9 +405,11 @@ public class VibratorManagerServiceTest {
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
- assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+ assertTrue(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
- assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS));
+ assertTrue(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, null, ALARM_ATTRS));
assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1));
assertNull(mVibratorProviders.get(2).getAlwaysOnEffect(1));
@@ -392,7 +423,8 @@ public class VibratorManagerServiceTest {
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
- assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+ assertFalse(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1));
}
@@ -405,7 +437,8 @@ public class VibratorManagerServiceTest {
CombinedVibrationEffect effect = CombinedVibrationEffect.startSequential()
.addNext(0, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.combine();
- assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
+ assertFalse(createSystemReadyService().setAlwaysOnEffect(
+ UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
assertNull(mVibratorProviders.get(1).getAlwaysOnEffect(1));
}
@@ -413,7 +446,7 @@ public class VibratorManagerServiceTest {
@Test
public void setAlwaysOnEffect_withNoVibratorWithCapability_ignoresEffect() {
mockVibrators(1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect mono = CombinedVibrationEffect.createSynced(
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -435,18 +468,18 @@ public class VibratorManagerServiceTest {
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(40, 1), RINGTONE_ATTRS);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
- service = createService();
+ service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(40, 10), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
- service = createService();
+ service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(40, 100), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -459,7 +492,7 @@ public class VibratorManagerServiceTest {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
@@ -480,7 +513,7 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
@@ -496,7 +529,7 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
@@ -534,7 +567,7 @@ public class VibratorManagerServiceTest {
when(mIInputManagerMock.getVibratorIds(eq(1))).thenReturn(new int[]{1});
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.createOneShot(10, 10));
@@ -550,7 +583,7 @@ public class VibratorManagerServiceTest {
public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
// The native callback will be dispatched manually in this test.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
@@ -573,7 +606,7 @@ public class VibratorManagerServiceTest {
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
// The native callback will be dispatched manually in this test.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
@@ -619,7 +652,7 @@ public class VibratorManagerServiceTest {
FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -645,7 +678,7 @@ public class VibratorManagerServiceTest {
mockVibrators(1, 2);
FakeVibratorControllerProvider fakeVibrator1 = mVibratorProviders.get(1);
fakeVibrator1.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -665,7 +698,7 @@ public class VibratorManagerServiceTest {
mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
mockVibrators(1, 2);
when(mNativeWrapperMock.prepareSynced(any())).thenReturn(false);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
@@ -686,7 +719,7 @@ public class VibratorManagerServiceTest {
mockVibrators(1, 2);
when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
when(mNativeWrapperMock.triggerSynced(anyLong())).thenReturn(false);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
CombinedVibrationEffect effect = CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.createOneShot(10, 50))
@@ -716,7 +749,7 @@ public class VibratorManagerServiceTest {
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
vibrate(service, CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -762,7 +795,7 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
mockVibrators(1, 2);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
vibrate(service,
CombinedVibrationEffect.startSynced()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
@@ -780,7 +813,7 @@ public class VibratorManagerServiceTest {
@Test
public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception {
mockVibrators(1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -793,7 +826,7 @@ public class VibratorManagerServiceTest {
@Test
public void cancelVibrate_stopsVibrating() throws Exception {
mockVibrators(1);
- VibratorManagerService service = createService();
+ VibratorManagerService service = createSystemReadyService();
service.cancelVibrate(service);
assertFalse(service.isVibrating(1));
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/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 47cf53b621d3..074ef3667857 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -323,4 +323,16 @@ public class DisplayPolicyTests extends WindowTestsBase {
assertFalse(navBarSource.getFrame().isEmpty());
assertTrue(imeSource.getFrame().contains(navBarSource.getFrame()));
}
+
+ @UseTestDisplay
+ @Test
+ public void testDisplayPolicyNotCrash() {
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+
+ // Verify if modules initialized after DisplayContent ctr throws NPE.
+ displayPolicy.onDisplayInfoChanged(mDisplayInfo);
+ displayPolicy.onConfigurationChanged();
+ displayPolicy.onOverlayChangedLw();
+ displayPolicy.release();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index be036034542e..80961d7afb70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -416,6 +416,16 @@ public class InsetsStateControllerTest extends WindowTestsBase {
verify(navBar, atLeastOnce()).notifyInsetsChanged();
}
+ @Test
+ public void testDispatchGlobalInsets() {
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ assertNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+ app.mAttrs.receiveInsetsIgnoringZOrder = true;
+ assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_NAVIGATION_BAR));
+ }
+
private WindowState createTestWindow(String name) {
final WindowState win = createWindow(null, TYPE_APPLICATION, name);
win.setHasSurface(true);
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/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b73c66407874..2f1d7eb404ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -655,7 +655,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
final Rect activityBounds = new Rect(mActivity.getBounds());
@@ -676,6 +676,96 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
+ // orientation letterbox.
+ mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
+ mActivity.info.minAspectRatio = 3;
+ prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Activity bounds should respect minimum aspect ratio for activity.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio),
+ activityBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
+ // orientation letterbox.
+ mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(3);
+ prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Activity bounds should respect maximum aspect ratio for activity.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio),
+ activityBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
+ // orientation letterbox.
+ final float fixedOrientationLetterboxAspectRatio = 1.1f;
+ mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(
+ fixedOrientationLetterboxAspectRatio);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Activity bounds should respect aspect ratio override for fixed orientation letterbox.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals((int) Math.rint(displayBounds.height() / fixedOrientationLetterboxAspectRatio),
+ activityBounds.width());
+ }
+
+ @Test
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
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 a1e5afb8b758..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
// =====================================
@@ -521,6 +583,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN);
}
+
@Test
public void testKeepsPictureInPictureLaunchModeInOptions() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
@@ -588,11 +651,14 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
}
@Test
- public void testNonEmptyLayoutInfersFreeformWithEmptySize() {
+ public void testLayoutWithGravityAndEmptySizeInfersFreeformAndRespectsCurrentSize() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
+ final Rect expectedLaunchBounds = new Rect(0, 0, 200, 100);
+
mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+ mCurrent.mBounds.set(expectedLaunchBounds);
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
.setGravity(Gravity.LEFT).build();
@@ -600,6 +666,9 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setLayout(layout).calculate());
+ assertEquals(expectedLaunchBounds.width(), mResult.mBounds.width());
+ assertEquals(expectedLaunchBounds.height(), mResult.mBounds.height());
+
assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
WINDOWING_MODE_FREEFORM);
}
@@ -1358,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);
@@ -1580,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;
@@ -1611,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..79ef8680dfec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -25,11 +25,14 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.isIndependent;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -316,9 +319,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
@@ -343,6 +346,76 @@ public class TransitionTests extends WindowTestsBase {
tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
}
+ @Test
+ public void testIndependent() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+ ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
+
+ final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task openInOpenTask = createTaskInStack(openTask, 0);
+ final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
+
+ final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ final Task changeInChangeTask = createTaskInStack(changeTask, 0);
+ final Task openInChangeTask = createTaskInStack(changeTask, 0);
+ final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
+ final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
+ // set organizer for everything so that they all get added to transition info
+ for (Task t : new Task[]{
+ openTask, openInOpenTask, changeTask, changeInChangeTask, openInChangeTask}) {
+ t.mTaskOrganizer = mockOrg;
+ }
+
+ // Start states.
+ changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(changeTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(openInOpenTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(openInChangeTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(changeInChangeTask,
+ new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(openInOpen, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(openInChange, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ fillChangeMap(changes, openTask);
+ // End states.
+ changeInChange.mVisibleRequested = true;
+ openInOpen.mVisibleRequested = true;
+ openInChange.mVisibleRequested = true;
+
+ int transit = TRANSIT_OLD_TASK_OPEN;
+ int flags = 0;
+
+ // Check full promotion from leaf
+ participants.add(changeInChange);
+ participants.add(openInOpen);
+ participants.add(openInChange);
+ // Explicitly add changeTask (to test independence with parents)
+ participants.add(changeTask);
+ ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ // Root changes should always be considered independent
+ assertTrue(isIndependent(
+ info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
+ assertTrue(isIndependent(
+ info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info));
+
+ // Children of a open/close change are not independent
+ assertFalse(isIndependent(
+ info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info));
+
+ // Non-root changes are not independent
+ assertFalse(isIndependent(
+ info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info));
+
+ // open/close within a change are independent
+ assertTrue(isIndependent(
+ info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
+ }
+
/** Fill the change map with all the parents of top. Change maps are usually fully populated */
private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
WindowContainer top) {
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 c13d6b19bf1d..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);
@@ -538,9 +546,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
/** Creates a {@link DisplayContent} and adds it to the system. */
private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy) {
- final DisplayContent display =
+ final DisplayContent dc =
new TestDisplayContent.Builder(mAtm, info).build();
- final DisplayContent dc = display.mDisplayContent;
// this display can show IME.
dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy);
return dc;
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 6aadd23d211f..8874e0afd716 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -20,19 +20,29 @@ import static android.Manifest.permission.MANAGE_UI_TRANSLATION;
import static android.content.Context.TRANSLATION_MANAGER_SERVICE;
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.translation.ITranslationManager;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager.UiTranslationState;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -48,6 +58,8 @@ public final class TranslationManagerService
private static final String TAG = "TranslationManagerService";
+ private static final int MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS = 2 * 60_000; // 2 minutes
+
public TranslationManagerService(Context context) {
// TODO: Discuss the disallow policy
super(context, new FrameworkResourcesServiceNameResolver(context,
@@ -60,19 +72,82 @@ public final class TranslationManagerService
return new TranslationManagerServiceImpl(this, mLock, resolvedUserId, disabled);
}
+ @Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_UI_TRANSLATION, TAG);
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS;
+ }
+
+ @Override
+ protected void dumpLocked(String prefix, PrintWriter pw) {
+ super.dumpLocked(prefix, pw);
+ }
+
private void enforceCallerHasPermission(String permission) {
final String msg = "Permission Denial from pid =" + Binder.getCallingPid() + ", uid="
+ Binder.getCallingUid() + " doesn't hold " + permission;
getContext().enforceCallingPermission(permission, msg);
}
+ /** True if the currently set handler service is not overridden by the shell. */
+ @GuardedBy("mLock")
+ private boolean isDefaultServiceLocked(int userId) {
+ final String defaultServiceName = mServiceNameResolver.getDefaultServiceName(userId);
+ if (defaultServiceName == null) {
+ return false;
+ }
+
+ final String currentServiceName = mServiceNameResolver.getServiceName(userId);
+ return defaultServiceName.equals(currentServiceName);
+ }
+
+ /** True if the caller of the api is the same app which hosts the TranslationService. */
+ @GuardedBy("mLock")
+ private boolean isCalledByServiceAppLocked(int userId, @NonNull String methodName) {
+ final int callingUid = Binder.getCallingUid();
+
+ final String serviceName = mServiceNameResolver.getServiceName(userId);
+ if (serviceName == null) {
+ Slog.e(TAG, methodName + ": called by UID " + callingUid
+ + ", but there's no service set for user " + userId);
+ return false;
+ }
+
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ if (serviceComponent == null) {
+ Slog.w(TAG, methodName + ": invalid service name: " + serviceName);
+ return false;
+ }
+
+ final String servicePackageName = serviceComponent.getPackageName();
+ final PackageManager pm = getContext().getPackageManager();
+ final int serviceUid;
+ try {
+ serviceUid = pm.getPackageUidAsUser(servicePackageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, methodName + ": could not verify UID for " + serviceName);
+ return false;
+ }
+ if (callingUid != serviceUid) {
+ Slog.e(TAG, methodName + ": called by UID " + callingUid + ", but service UID is "
+ + serviceUid);
+ return false;
+ }
+ return true;
+ }
+
final class TranslationManagerServiceStub extends ITranslationManager.Stub {
@Override
public void getSupportedLocales(IResultReceiver receiver, int userId)
throws RemoteException {
synchronized (mLock) {
final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service != null) {
+ if (service != null && (isDefaultServiceLocked(userId)
+ || isCalledByServiceAppLocked(userId, "getSupportedLocales"))) {
service.getSupportedLocalesLocked(receiver);
} else {
Slog.v(TAG, "getSupportedLocales(): no service for " + userId);
@@ -86,7 +161,8 @@ public final class TranslationManagerService
int sessionId, IResultReceiver receiver, int userId) throws RemoteException {
synchronized (mLock) {
final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service != null) {
+ if (service != null && (isDefaultServiceLocked(userId)
+ || isCalledByServiceAppLocked(userId, "onSessionCreated"))) {
service.onSessionCreatedLocked(sourceSpec, destSpec, sessionId, receiver);
} else {
Slog.v(TAG, "onSessionCreated(): no service for " + userId);
@@ -96,18 +172,58 @@ public final class TranslationManagerService
}
@Override
- public void updateUiTranslationState(@UiTranslationState int state,
+ public void updateUiTranslationStateByTaskId(@UiTranslationState int state,
TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
int taskId, int userId) {
+ // deprecated
enforceCallerHasPermission(MANAGE_UI_TRANSLATION);
synchronized (mLock) {
final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service != null) {
- service.updateUiTranslationState(state, sourceSpec, destSpec, viewIds,
+ if (service != null && (isDefaultServiceLocked(userId)
+ || isCalledByServiceAppLocked(userId,
+ "updateUiTranslationStateByTaskId"))) {
+ service.updateUiTranslationStateLocked(state, sourceSpec, destSpec, viewIds,
taskId);
}
}
}
+
+ @Override
+ public void updateUiTranslationState(@UiTranslationState int state,
+ TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+ IBinder token, int taskId, int userId) {
+ enforceCallerHasPermission(MANAGE_UI_TRANSLATION);
+ synchronized (mLock) {
+ final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null && (isDefaultServiceLocked(userId)
+ || isCalledByServiceAppLocked(userId, "updateUiTranslationState"))) {
+ service.updateUiTranslationStateLocked(state, sourceSpec, destSpec, viewIds,
+ token, taskId);
+ }
+ }
+ }
+
+ /**
+ * Dump the service state into the given stream. You run "adb shell dumpsys translation".
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in,
+ @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args,
+ @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new TranslationManagerServiceShellCommand(
+ TranslationManagerService.this).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
}
@Override // from SystemService
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 38be85c92197..ab6ac12c90fa 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.IBinder;
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
@@ -133,18 +134,40 @@ final class TranslationManagerServiceImpl extends
}
@GuardedBy("mLock")
- public void updateUiTranslationState(@UiTranslationState int state,
+ public void updateUiTranslationStateLocked(@UiTranslationState int state,
TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
int taskId) {
- // TODO(b/177394471): use taskId as a temporary solution. The solution may use a token to
- // content capture manager service find the activitytoken. Then we can use this
- // activitytoken to find the activity to callback. But we need to change cc API so use
- // temporary solution.
- final ActivityTokens tokens = mActivityTaskManagerInternal.getTopActivityForTask(taskId);
- if (tokens == null) {
+ // deprecated
+ final ActivityTokens taskTopActivityTokens =
+ mActivityTaskManagerInternal.getTopActivityForTask(taskId);
+ if (taskTopActivityTokens == null) {
Slog.w(TAG, "Unknown activity to query for update translation state.");
return;
}
+ updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, destSpec,
+ viewIds);
+ }
+
+ @GuardedBy("mLock")
+ public void updateUiTranslationStateLocked(@UiTranslationState int state,
+ TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+ IBinder token, int taskId) {
+ // Get top activity for a given task id
+ final ActivityTokens taskTopActivityTokens =
+ mActivityTaskManagerInternal.getTopActivityForTask(taskId);
+ if (taskTopActivityTokens == null
+ || taskTopActivityTokens.getShareableActivityToken() != token) {
+ Slog.w(TAG, "Unknown activity or it was finished to query for update "
+ + "translation state for token=" + token + " taskId=" + taskId);
+ return;
+ }
+ updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, destSpec,
+ viewIds);
+ }
+
+ private void updateUiTranslationStateByActivityTokens(ActivityTokens tokens,
+ @UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec destSpec,
+ List<AutofillId> viewIds) {
try {
tokens.getApplicationThread().updateUiTranslationState(tokens.getActivityToken(), state,
sourceSpec, destSpec, viewIds);
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceShellCommand.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceShellCommand.java
new file mode 100644
index 000000000000..ba1b3908a91e
--- /dev/null
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceShellCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.translation;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/** Handles adb shell commands send to TranslationManagerService. */
+public class TranslationManagerServiceShellCommand extends ShellCommand {
+ private final TranslationManagerService mService;
+
+ TranslationManagerServiceShellCommand(TranslationManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ if ("set".equals(cmd)) {
+ return requestSet(pw);
+ }
+ return handleDefaultCommands(cmd);
+ }
+
+ private int requestSet(PrintWriter pw) {
+ final String what = getNextArgRequired();
+ if ("temporary-service".equals(what)) {
+ return setTemporaryService(pw);
+ }
+ pw.println("Invalid set: " + what);
+ return -1;
+ }
+
+ private int setTemporaryService(PrintWriter pw) {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("TranslationService temporarily set to " + serviceName + " for "
+ + duration + "ms");
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.println("Translation Service (translation) commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ }
+ }
+}
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/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl b/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl
new file mode 100644
index 000000000000..685fe9c927b1
--- /dev/null
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable BluetoothCallQualityReport;
diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
index 10339a818205..8703d84831ff 100644
--- a/telecomm/java/android/telecom/BluetoothCallQualityReport.java
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
@@ -24,6 +24,8 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* This class represents the quality report that bluetooth framework sends
* whenever there's a bad voice quality is detected from their side.
@@ -145,6 +147,26 @@ public final class BluetoothCallQualityReport implements Parcelable {
}
};
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ BluetoothCallQualityReport that = (BluetoothCallQualityReport) o;
+ return mSentTimestampMillis == that.mSentTimestampMillis
+ && mChoppyVoice == that.mChoppyVoice && mRssiDbm == that.mRssiDbm
+ && mSnrDb == that.mSnrDb
+ && mRetransmittedPacketsCount == that.mRetransmittedPacketsCount
+ && mPacketsNotReceivedCount == that.mPacketsNotReceivedCount
+ && mNegativeAcknowledgementCount == that.mNegativeAcknowledgementCount;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSentTimestampMillis, mChoppyVoice, mRssiDbm, mSnrDb,
+ mRetransmittedPacketsCount, mPacketsNotReceivedCount,
+ mNegativeAcknowledgementCount);
+ }
+
/**
* Builder class for {@link ConnectionRequest}
*/
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 044ea80cba4b..2a5ddfd891b1 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -267,6 +267,64 @@ public final class Call {
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
+ /**
+ * Event reported from the Telecom stack to report an in-call diagnostic message which the
+ * dialer app may opt to display to the user. A diagnostic message is used to communicate
+ * scenarios the device has detected which may impact the quality of the ongoing call.
+ * <p>
+ * For example a problem with a bluetooth headset may generate a recommendation for the user to
+ * try using the speakerphone instead, or if the device detects it has entered a poor service
+ * area, the user might be warned so that they can finish their call prior to it dropping.
+ * <p>
+ * A diagnostic message is considered persistent in nature. When the user enters a poor service
+ * area, for example, the accompanying diagnostic message persists until they leave the area
+ * of poor service. Each message is accompanied with a {@link #EXTRA_DIAGNOSTIC_MESSAGE_ID}
+ * which uniquely identifies the diagnostic condition being reported. The framework raises a
+ * call event of type {@link #EVENT_CLEAR_DIAGNOSTIC_MESSAGE} when the condition reported has
+ * been cleared. The dialer app should display the diagnostic message until it is cleared.
+ * If multiple diagnostic messages are sent with different IDs (which have not yet been cleared)
+ * the dialer app should prioritize the most recently received message, but still provide the
+ * user with a means to review past messages.
+ * <p>
+ * The text of the message is found in {@link #EXTRA_DIAGNOSTIC_MESSAGE} in the form of a human
+ * readable {@link CharSequence} which is intended for display in the call UX.
+ * <p>
+ * The telecom framework audibly notifies the user of the presence of a diagnostic message, so
+ * the dialer app needs only to concern itself with visually displaying the message.
+ * <p>
+ * The dialer app receives this event via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public static final String EVENT_DISPLAY_DIAGNOSTIC_MESSAGE =
+ "android.telecom.event.DISPLAY_DIAGNOSTIC_MESSAGE";
+
+ /**
+ * Event reported from the telecom framework when a diagnostic message previously raised with
+ * {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE} has cleared and is no longer pertinent.
+ * <p>
+ * The {@link #EXTRA_DIAGNOSTIC_MESSAGE_ID} indicates the diagnostic message which has been
+ * cleared.
+ * <p>
+ * The dialer app receives this event via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public static final String EVENT_CLEAR_DIAGNOSTIC_MESSAGE =
+ "android.telecom.event.CLEAR_DIAGNOSTIC_MESSAGE";
+
+ /**
+ * Integer extra representing a message ID for a message posted via
+ * {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE}. Used to ensure that the dialer app knows when
+ * the message in question has cleared via {@link #EVENT_CLEAR_DIAGNOSTIC_MESSAGE}.
+ */
+ public static final String EXTRA_DIAGNOSTIC_MESSAGE_ID =
+ "android.telecom.extra.DIAGNOSTIC_MESSAGE_ID";
+
+ /**
+ * {@link CharSequence} extra used with {@link #EVENT_DISPLAY_DIAGNOSTIC_MESSAGE}. This is the
+ * diagnostic message the dialer app should display.
+ */
+ public static final String EXTRA_DIAGNOSTIC_MESSAGE =
+ "android.telecom.extra.DIAGNOSTIC_MESSAGE";
/**
* Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java
new file mode 100644
index 000000000000..201c5db74e16
--- /dev/null
+++ b/telecomm/java/android/telecom/CallDiagnosticService.java
@@ -0,0 +1,328 @@
+/*
+ * 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.telecom;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.telecom.ICallDiagnosticService;
+import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
+
+import java.util.Map;
+
+/**
+ * The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the
+ * {@code call_diagnostic_service_package_name} key in the
+ * {@code packages/services/Telecomm/res/values/config.xml} file. An OEM can use this API to help
+ * provide more actionable information about calling issues the user encounters during and after
+ * a call.
+ *
+ * <h1>Manifest Declaration</h1>
+ * The following is an example of how to declare the service entry in the
+ * {@link CallDiagnosticService} manifest file:
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourCallDiagnosticServiceImplementation"
+ * android:permission="android.permission.BIND_CALL_DIAGNOSTIC_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.CallDiagnosticService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class CallDiagnosticService extends Service {
+
+ /**
+ * Binder stub implementation which handles incoming requests from Telecom.
+ */
+ private final class CallDiagnosticServiceBinder extends ICallDiagnosticService.Stub {
+
+ @Override
+ public void setAdapter(ICallDiagnosticServiceAdapter adapter) throws RemoteException {
+ handleSetAdapter(adapter);
+ }
+
+ @Override
+ public void initializeDiagnosticCall(ParcelableCall call) throws RemoteException {
+ handleCallAdded(call);
+ }
+
+ @Override
+ public void updateCall(ParcelableCall call) throws RemoteException {
+ handleCallUpdated(call);
+ }
+
+ @Override
+ public void removeDiagnosticCall(String callId) throws RemoteException {
+ handleCallRemoved(callId);
+ }
+
+ @Override
+ public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException {
+ onCallAudioStateChanged(callAudioState);
+ }
+
+ @Override
+ public void receiveDeviceToDeviceMessage(String callId, int message, int value) {
+ handleReceivedD2DMessage(callId, message, value);
+ }
+
+ @Override
+ public void receiveBluetoothCallQualityReport(BluetoothCallQualityReport qualityReport)
+ throws RemoteException {
+ handleBluetoothCallQualityReport(qualityReport);
+ }
+ }
+
+ /**
+ * Listens to events raised by a {@link DiagnosticCall}.
+ */
+ private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener =
+ new android.telecom.DiagnosticCall.Listener() {
+
+ @Override
+ public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall,
+ @DiagnosticCall.MessageType int message, int value) {
+ handleSendDeviceToDeviceMessage(diagnosticCall, message, value);
+ }
+
+ @Override
+ public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message) {
+ handleDisplayDiagnosticMessage(diagnosticCall, messageId, message);
+ }
+
+ @Override
+ public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
+ handleClearDiagnosticMessage(diagnosticCall, messageId);
+ }
+ };
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService";
+
+ /**
+ * Map which tracks the Telecom calls received from the Telecom stack.
+ */
+ private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>();
+ private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>();
+ private ICallDiagnosticServiceAdapter mAdapter;
+
+ @Nullable
+ @Override
+ public IBinder onBind(@NonNull Intent intent) {
+ Log.i(this, "onBind!");
+ return new CallDiagnosticServiceBinder();
+ }
+
+ /**
+ * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call
+ * which was added to Telecom.
+ * <p>
+ * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be
+ * used for the lifespan of this call.
+ *
+ * @param call The details of the new call.
+ * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService}
+ * provides to be used for the lifespan of the call.
+ * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned.
+ */
+ public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull
+ android.telecom.Call.Details call);
+
+ /**
+ * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed.
+ * This happens when Telecom is no longer tracking the call in question.
+ * @param call The diagnostic call which is no longer tracked by Telecom.
+ */
+ public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call);
+
+ /**
+ * Telecom calls this method when the audio routing or available audio route information
+ * changes.
+ * <p>
+ * Audio state is common to all calls.
+ *
+ * @param audioState The new audio state.
+ */
+ public abstract void onCallAudioStateChanged(
+ @NonNull CallAudioState audioState);
+
+ /**
+ * Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the
+ * bluetooth stack.
+ * @param qualityReport the {@link BluetoothCallQualityReport}.
+ */
+ public abstract void onBluetoothCallQualityReportReceived(
+ @NonNull BluetoothCallQualityReport qualityReport);
+
+ /**
+ * Handles a request from Telecom to set the adapater used to communicate back to Telecom.
+ * @param adapter
+ */
+ private void handleSetAdapter(@NonNull ICallDiagnosticServiceAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Handles a request from Telecom to add a new call.
+ * @param parcelableCall
+ */
+ private void handleCallAdded(@NonNull ParcelableCall parcelableCall) {
+ String telecomCallId = parcelableCall.getId();
+ Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId);
+ Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+
+ DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails);
+ if (diagnosticCall == null) {
+ throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided.");
+ }
+ diagnosticCall.setListener(mDiagnosticCallListener);
+ diagnosticCall.setCallId(telecomCallId);
+ mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall);
+ }
+
+ /**
+ * Handles an update to {@link Call.Details} notified by Telecom.
+ * Caches the call details and notifies the {@link DiagnosticCall} of the change via
+ * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}.
+ * @param parcelableCall the new parceled call details from Telecom.
+ */
+ private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) {
+ String telecomCallId = parcelableCall.getId();
+ Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId);
+ Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall);
+
+ DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId);
+ mCallByTelecomCallId.put(telecomCallId, newCallDetails);
+ diagnosticCall.handleCallUpdated(newCallDetails);
+ }
+
+ /**
+ * Handles a request from Telecom to remove an existing call.
+ * @param telecomCallId
+ */
+ private void handleCallRemoved(@NonNull String telecomCallId) {
+ Log.i(this, "handleCallRemoved: callId=%s - removed", telecomCallId);
+
+ if (mCallByTelecomCallId.containsKey(telecomCallId)) {
+ mCallByTelecomCallId.remove(telecomCallId);
+ }
+ if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) {
+ DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId);
+ // Inform the service of the removed call.
+ onRemoveDiagnosticCall(call);
+ }
+ }
+
+ /**
+ * Handles an incoming device to device message received from Telecom. Notifies the
+ * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}.
+ * @param callId
+ * @param message
+ * @param value
+ */
+ private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) {
+ Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value);
+ DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId);
+ diagnosticCall.onReceiveDeviceToDeviceMessage(message, value);
+ }
+
+ /**
+ * Handles an incoming bluetooth call quality report from Telecom. Notifies via
+ * {@link CallDiagnosticService#onBluetoothCallQualityReportReceived(
+ * BluetoothCallQualityReport)}.
+ * @param qualityReport The bluetooth call quality remote.
+ */
+ private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport
+ qualityReport) {
+ Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport);
+ onBluetoothCallQualityReportReceived(qualityReport);
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to send a device to device message (received
+ * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}.
+ * @param diagnosticCall
+ * @param message
+ * @param value
+ */
+ private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall,
+ int message, int value) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.sendDeviceToDeviceMessage(callId, message, value);
+ Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message,
+ value);
+ } catch (RemoteException e) {
+ Log.w(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d failed %s",
+ callId, message, value, e);
+ }
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message.
+ * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}.
+ * @param diagnosticCall
+ * @param messageId
+ * @param message
+ */
+ private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.displayDiagnosticMessage(callId, messageId, message);
+ Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId,
+ message);
+ } catch (RemoteException e) {
+ Log.w(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s failed %s",
+ callId, messageId, message, e);
+ }
+ }
+
+ /**
+ * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic
+ * message.
+ * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}.
+ * @param diagnosticCall
+ * @param messageId
+ */
+ private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) {
+ String callId = diagnosticCall.getCallId();
+ try {
+ mAdapter.clearDiagnosticMessage(callId, messageId);
+ Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId);
+ } catch (RemoteException e) {
+ Log.w(this, "handleClearDiagnosticMessage: call=%s; msg=%d failed %s",
+ callId, messageId, e);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 7c6253ce933a..335857af8883 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -938,6 +938,46 @@ public abstract class Connection extends Conferenceable {
public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED =
"android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
+ /**
+ * Connection event used to signal between the telephony {@link ConnectionService} and Telecom
+ * when device to device messages are sent/received.
+ * <p>
+ * Device to device messages originating from the network are sent by telephony using
+ * {@link Connection#sendConnectionEvent(String, Bundle)} and are routed up to any active
+ * {@link CallDiagnosticService} implementation which is active.
+ * <p>
+ * Likewise, if a {@link CallDiagnosticService} sends a message using
+ * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony
+ * via {@link Connection#onCallEvent(String, Bundle)}. The telephony stack will relay the
+ * message to the other device.
+ * @hide
+ */
+ @SystemApi
+ public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE =
+ "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+
+ /**
+ * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
+ * message type.
+ *
+ * See {@link DiagnosticCall} for more information.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE =
+ "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
+
+ /**
+ * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device
+ * message value.
+ * <p>
+ * See {@link DiagnosticCall} for more information.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE =
+ "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java
new file mode 100644
index 000000000000..a4952899eb46
--- /dev/null
+++ b/telecomm/java/android/telecom/DiagnosticCall.java
@@ -0,0 +1,381 @@
+/*
+ * 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.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic
+ * information about a mobile call on the device. The {@link CallDiagnosticService} can generate
+ * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API
+ * which provides the user with valuable information about conditions impacting their call and
+ * corrective actions. For example, if the {@link CallDiagnosticService} determines that conditions
+ * on the call are degrading, it can inform the user that the call may soon drop and that they
+ * can try using a different calling method (e.g. VOIP or WIFI).
+ * @hide
+ */
+@SystemApi
+public abstract class DiagnosticCall {
+
+ /**
+ * @hide
+ */
+ public interface Listener {
+ void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value);
+ void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId,
+ CharSequence message);
+ void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId);
+ }
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type
+ * used for the current call. Based loosely on the
+ * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a
+ * high level summary of the call radio access type.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #NETWORK_TYPE_LTE}</LI>
+ * <LI>{@link #NETWORK_TYPE_IWLAN}</LI>
+ * <LI>{@link #NETWORK_TYPE_NR}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_NETWORK_TYPE = 1;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec
+ * used for the current call. Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a
+ * call.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #AUDIO_CODEC_EVS}</LI>
+ * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI>
+ * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of
+ * the device. Will typically mirror battery state reported via intents such as
+ * {@link android.content.Intent#ACTION_BATTERY_LOW}.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #BATTERY_STATE_LOW}</LI>
+ * <LI>{@link #BATTERY_STATE_GOOD}</LI>
+ * <LI>{@link #BATTERY_STATE_CHARGING}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
+
+ /**
+ * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via
+ * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network
+ * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal
+ * poor coverage if the network coverage reaches a level where there is a high probability of
+ * the call dropping as a result.
+ * <p>
+ * Valid values:
+ * <UL>
+ * <LI>{@link #COVERAGE_POOR}</LI>
+ * <LI>{@link #COVERAGE_GOOD}</LI>
+ * </UL>
+ */
+ public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "MESSAGE_", value = {
+ MESSAGE_CALL_NETWORK_TYPE,
+ MESSAGE_CALL_AUDIO_CODEC,
+ MESSAGE_DEVICE_BATTERY_STATE,
+ MESSAGE_DEVICE_NETWORK_COVERAGE
+ })
+ public @interface MessageType {}
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the
+ * call.
+ */
+ public static final int NETWORK_TYPE_LTE = 1;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call.
+ */
+ public static final int NETWORK_TYPE_IWLAN = 2;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in
+ * used for the call.
+ */
+ public static final int NETWORK_TYPE_NR = 3;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the
+ * Enhanced Voice Services (EVS) codec for the call.
+ */
+ public static final int AUDIO_CODEC_EVS = 1;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
+ * (adaptive multi-rate) WB (wide band) audio codec.
+ */
+ public static final int AUDIO_CODEC_AMR_WB = 2;
+
+ /**
+ * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR
+ * (adaptive multi-rate) NB (narrow band) audio codec.
+ */
+ public static final int AUDIO_CODEC_AMR_NB = 3;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low.
+ */
+ public static final int BATTERY_STATE_LOW = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low.
+ */
+ public static final int BATTERY_STATE_GOOD = 2;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging.
+ */
+ public static final int BATTERY_STATE_CHARGING = 3;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor.
+ */
+ public static final int COVERAGE_POOR = 1;
+
+ /**
+ * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good.
+ */
+ public static final int COVERAGE_GOOD = 2;
+
+ private Listener mListener;
+ private String mCallId;
+ private Call.Details mCallDetails;
+
+ /**
+ * @hide
+ */
+ public void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Sets the call ID for this {@link DiagnosticCall}.
+ * @param callId
+ * @hide
+ */
+ public void setCallId(@NonNull String callId) {
+ mCallId = callId;
+ }
+
+ /**
+ * @return the Telecom call ID for this {@link DiagnosticCall}.
+ * @hide
+ */
+ public @NonNull String getCallId() {
+ return mCallId;
+ }
+
+ /**
+ * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as
+ * reported by {@link #onCallDetailsChanged(Call.Details)}.
+ * @return The latest {@link Call.Details}.
+ */
+ public @NonNull Call.Details getCallDetails() {
+ return mCallDetails;
+ }
+
+ /**
+ * Telecom calls this method when the details of a call changes.
+ */
+ public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details);
+
+ /**
+ * The {@link CallDiagnosticService} implements this method to handle messages received via
+ * device to device communication.
+ * <p>
+ * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device
+ * communication.
+ * <p>
+ * The underlying device to device communication protocol assumes that where there the two
+ * devices communicating are using a different version of the protocol, messages the recipient
+ * are not aware of are silently discarded. This means an older client talking to a new client
+ * will not receive newer messages and values sent by the new client.
+ */
+ public abstract void onReceiveDeviceToDeviceMessage(
+ @MessageType int message,
+ int value);
+
+ /**
+ * Sends a device to device message to the device on the other end of this call.
+ * <p>
+ * Device to device communication is an Android platform feature which supports low bandwidth
+ * communication between Android devices while they are in a call. The device to device
+ * communication leverages DTMF tones or RTP header extensions to pass messages. The
+ * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the
+ * nature of the message are informational only and are used only to convey basic state
+ * information between devices.
+ * <p>
+ * Device to device messages are intentional simplifications of more rich indicators in the
+ * platform due to the extreme bandwidth constraints inherent with underlying device to device
+ * communication transports used by the telephony framework. Device to device communication is
+ * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets
+ * for a call, or using the DTMF digits A-D as a communication pathway. Signalling requirements
+ * for DTMF digits place a significant limitation on the amount of information which can be
+ * communicated during a call.
+ * <p>
+ * Allowed message types and values are:
+ * <ul>
+ * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}
+ * <ul>
+ * <li>{@link #NETWORK_TYPE_LTE}</li>
+ * <li>{@link #NETWORK_TYPE_IWLAN}</li>
+ * <li>{@link #NETWORK_TYPE_NR}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}
+ * <ul>
+ * <li>{@link #AUDIO_CODEC_EVS}</li>
+ * <li>{@link #AUDIO_CODEC_AMR_WB}</li>
+ * <li>{@link #AUDIO_CODEC_AMR_NB}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}
+ * <ul>
+ * <li>{@link #BATTERY_STATE_LOW}</li>
+ * <li>{@link #BATTERY_STATE_GOOD}</li>
+ * <li>{@link #BATTERY_STATE_CHARGING}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}
+ * <ul>
+ * <li>{@link #COVERAGE_POOR}</li>
+ * <li>{@link #COVERAGE_GOOD}</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @param message The message type to send.
+ * @param value The message value corresponding to the type.
+ */
+ public final void sendDeviceToDeviceMessage(int message, int value) {
+ if (mListener != null) {
+ mListener.onSendDeviceToDeviceMessage(this, message, value);
+ }
+ }
+
+ /**
+ * Telecom calls this method when a GSM or CDMA call disconnects.
+ * The CallDiagnosticService can return a human readable disconnect message which will be passed
+ * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically
+ * shows this message at the termination of the call. If {@code null} is returned, the
+ * disconnect message generated by the telephony stack will be shown instead.
+ * <p>
+ * @param disconnectCause the disconnect cause for the call.
+ * @param preciseDisconnectCause the precise disconnect cause for the call.
+ * @return the disconnect message to use in place of the default Telephony message, or
+ * {@code null} if the default message will not be overridden.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @Annotation.DisconnectCauses int disconnectCause,
+ @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+
+ /**
+ * Telecom calls this method when an IMS call disconnects and Telephony has already
+ * provided the disconnect reason info and disconnect message for the call. The
+ * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and
+ * combine it with other call diagnostic information it is aware of to override the disconnect
+ * call message if desired.
+ *
+ * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection.
+ * @return A user-readable call disconnect message to use in place of the platform-generated
+ * disconnect message, or {@code null} if the disconnect message should not be overridden.
+ */
+ // TODO: Wire in Telephony support for this.
+ public abstract @Nullable CharSequence onCallDisconnected(
+ @NonNull ImsReasonInfo disconnectReason);
+
+ /**
+ * Telecom calls this method when a {@link CallQuality} report is received from the telephony
+ * stack for a call.
+ * @param callQuality The call quality report for this call.
+ */
+ public abstract void onCallQualityReceived(@NonNull CallQuality callQuality);
+
+ /**
+ * Signals the active default dialer app to display a call diagnostic message. This can be
+ * used to report problems encountered during the span of a call.
+ * <p>
+ * The {@link CallDiagnosticService} provides a unique client-specific identifier used to
+ * identify the specific diagnostic message type.
+ * <p>
+ * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the
+ * diagnostic condition has cleared.
+ * @param messageId the unique message identifier.
+ * @param message a human-readable, localized message to be shown to the user indicating a
+ * call issue which has occurred, along with potential mitigating actions.
+ */
+ public final void displayDiagnosticMessage(int messageId, @NonNull
+ CharSequence message) {
+ if (mListener != null) {
+ mListener.onDisplayDiagnosticMessage(this, messageId, message);
+ }
+ }
+
+ /**
+ * Signals to the active default dialer that the diagnostic message previously signalled using
+ * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no
+ * longer applicable (e.g. service has improved, for example.
+ * @param messageId the message identifier for a message previously shown via
+ * {@link #displayDiagnosticMessage(int, CharSequence)}.
+ */
+ public final void clearDiagnosticMessage(int messageId) {
+ if (mListener != null) {
+ mListener.onClearDiagnosticMessage(this, messageId);
+ }
+ }
+
+ /**
+ * Called by the {@link CallDiagnosticService} to update the call details for this
+ * {@link DiagnosticCall} based on an update received from Telecom.
+ * @param newDetails the new call details.
+ * @hide
+ */
+ public void handleCallUpdated(@NonNull Call.Details newDetails) {
+ mCallDetails = newDetails;
+ onCallDetailsChanged(newDetails);
+ }
+}
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 2a4fdcb1475d..922eddb6ac3e 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -522,7 +522,7 @@ public class Log {
return "";
}
return Arrays.stream(packageName.split("\\."))
- .map(s -> s.substring(0,1))
+ .map(s -> s.length() == 0 ? "" : s.substring(0, 1))
.collect(Collectors.joining(""));
}
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
new file mode 100644
index 000000000000..65b4d19b3d9b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.internal.telecom;
+
+import android.telecom.BluetoothCallQualityReport;
+import android.telecom.CallAudioState;
+import android.telecom.ParcelableCall;
+import com.android.internal.telecom.ICallDiagnosticServiceAdapter;
+
+/**
+ * Internal remote interface for a call diagnostic service.
+ * @see android.telecom.CallDiagnosticService
+ * @hide
+ */
+oneway interface ICallDiagnosticService {
+ void setAdapter(in ICallDiagnosticServiceAdapter adapter);
+ void initializeDiagnosticCall(in ParcelableCall call);
+ void updateCall(in ParcelableCall call);
+ void updateCallAudioState(in CallAudioState callAudioState);
+ void removeDiagnosticCall(in String callId);
+ void receiveDeviceToDeviceMessage(in String callId, int message, int value);
+ void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl
new file mode 100644
index 000000000000..92eec2a95430
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticServiceAdapter.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.internal.telecom;
+
+import android.telecom.CallAudioState;
+import android.telecom.ParcelableCall;
+
+/**
+ * Remote interface for messages from the CallDiagnosticService to the platform.
+ * @see android.telecom.CallDiagnosticService
+ * @hide
+ */
+oneway interface ICallDiagnosticServiceAdapter {
+ void displayDiagnosticMessage(in String callId, int messageId, in CharSequence message);
+ void clearDiagnosticMessage(in String callId, int messageId);
+ void sendDeviceToDeviceMessage(in String callId, int message, int value);
+ void overrideDisconnectMessage(in String callId, in CharSequence message);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index eb106b50f69d..78283fa73514 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -353,4 +353,8 @@ interface ITelecomService {
*/
void setTestDefaultDialer(in String packageName);
+ /**
+ * @see TelecomServiceImpl#setTestCallDiagnosticService
+ */
+ void setTestCallDiagnosticService(in String packageName);
}
diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
index c7e7cd5ec64e..179248dd2cf9 100644
--- a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -65,6 +65,11 @@ public class SipMessageParsingUtils {
// compact form of the via header key
private static final String VIA_SIP_HEADER_KEY_COMPACT = "v";
+ // call-id header key
+ private static final String CALL_ID_SIP_HEADER_KEY = "call-id";
+ // compact form of the call-id header key
+ private static final String CALL_ID_SIP_HEADER_KEY_COMPACT = "i";
+
/**
* @return true if the SIP message start line is considered a request (based on known request
* methods).
@@ -124,6 +129,17 @@ public class SipMessageParsingUtils {
return null;
}
+ /**
+ * Return the call-id header key's associated value.
+ * @param headerString The string containing the headers of the SIP message.
+ */
+ public static String getCallId(String headerString) {
+ // search for the call-Id header, there should only be one in the header.
+ List<Pair<String, String>> headers = parseHeaders(headerString, true,
+ CALL_ID_SIP_HEADER_KEY, CALL_ID_SIP_HEADER_KEY_COMPACT);
+ return !headers.isEmpty() ? headers.get(0).second : null;
+ }
+
private static String[] splitStartLineAndVerify(String startLine) {
String[] splitLine = startLine.split(" ");
if (isStartLineMalformed(splitLine)) return null;
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 706e3cb93a0f..a78f81331c8c 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -371,6 +371,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
* Get the 5G NR connection state.
*
* @return the 5G NR connection state.
+ * @hide
*/
public @NRState int getNrState() {
return mNrState;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1473b7a8873d..cf4e6779b363 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -891,6 +891,14 @@ public class SubscriptionManager {
public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
/**
+ * TelephonyProvider column name for VoIMS opt-in status.
+ *
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS;
+
+ /**
* Profile class of the subscription
* @hide
*/
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 5b5570b27a9b..2f89bfb60d3d 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -30,8 +30,8 @@ import java.util.Objects;
* necessarily a precise or accurate representation of the current state and should be treated
* accordingly.
* To be notified of changes in TelephonyDisplayInfo, use
- * {@link TelephonyManager#registerPhoneStateListener} with a {@link PhoneStateListener}
- * that implements {@link PhoneStateListener.DisplayInfoChangedListener}.
+ * {@link TelephonyManager#registerTelephonyCallback} with a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.DisplayInfoListener}.
* Override the onDisplayInfoChanged() method to handle the broadcast.
*/
public final class TelephonyDisplayInfo implements Parcelable {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 61e809b55031..c48bd211fac2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5949,25 +5949,20 @@ public class TelephonyManager {
* @param events The telephony state(s) of interest to the listener,
* as a bitwise-OR combination of {@link PhoneStateListener}
* LISTEN_ flags.
- * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}.
*/
@Deprecated
public void listen(PhoneStateListener listener, int events) {
- if (!listener.isExecutorSet()) {
- throw new IllegalStateException("PhoneStateListener should be created on a thread "
- + "with Looper.myLooper() != null");
- }
- boolean notifyNow = getITelephony() != null;
- mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
- if (mTelephonyRegistryMgr != null) {
- if (events != PhoneStateListener.LISTEN_NONE) {
- mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId,
- getOpPackageName(), getAttributionTag(), listener, events, notifyNow);
- } else {
- unregisterPhoneStateListener(listener);
- }
+ if (mContext == null) return;
+ boolean notifyNow = (getITelephony() != null);
+ TelephonyRegistryManager telephonyRegistry =
+ (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistry != null) {
+ telephonyRegistry.listenFromListener(mSubId, getOpPackageName(),
+ getAttributionTag(), listener, events, notifyNow);
} else {
- throw new IllegalStateException("telephony service is null.");
+ Rlog.w(TAG, "telephony registry not ready.");
}
}
@@ -9129,18 +9124,11 @@ public class TelephonyManager {
*/
public static final int CALL_COMPOSER_STATUS_ON = 1;
- /**
- * Call composer status indicating that sending/receiving pictures is disabled.
- * All other attachments are still enabled in this state.
- */
- public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2;
-
/** @hide */
@IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
value = {
CALL_COMPOSER_STATUS_ON,
CALL_COMPOSER_STATUS_OFF,
- CALL_COMPOSER_STATUS_ON_NO_PICTURES,
})
public @interface CallComposerStatus {}
@@ -9148,9 +9136,8 @@ public class TelephonyManager {
* Set the user-set status for enriched calling with call composer.
*
* @param status user-set status for enriched calling with call composer;
- * it must be any of {@link #CALL_COMPOSER_STATUS_ON}
- * {@link #CALL_COMPOSER_STATUS_OFF},
- * or {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}
+ * it must be either {@link #CALL_COMPOSER_STATUS_ON} or
+ * {@link #CALL_COMPOSER_STATUS_OFF}.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -9160,7 +9147,7 @@ public class TelephonyManager {
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCallComposerStatus(@CallComposerStatus int status) {
- if (status > CALL_COMPOSER_STATUS_ON_NO_PICTURES
+ if (status > CALL_COMPOSER_STATUS_ON
|| status < CALL_COMPOSER_STATUS_OFF) {
throw new IllegalArgumentException("requested status is invalid");
}
@@ -9183,9 +9170,8 @@ public class TelephonyManager {
*
* @throws SecurityException if the caller does not have the permission.
*
- * @return the user-set status for enriched calling with call composer, any of
- * {@link #CALL_COMPOSER_STATUS_ON}, {@link #CALL_COMPOSER_STATUS_OFF}, or
- * {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}.
+ * @return the user-set status for enriched calling with call composer, either of
+ * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CallComposerStatus int getCallComposerStatus() {
@@ -15058,66 +15044,69 @@ public class TelephonyManager {
}
/**
- * Registers a listener object to receive notification of changes
- * in specified telephony states.
+ * Registers a callback object to receive notification of changes in specified telephony states.
* <p>
- * To register a listener, pass a {@link PhoneStateListener} which implements
+ * To register a callback, pass a {@link TelephonyCallback} which implements
* interfaces of events. For example,
- * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
- * {@link PhoneStateListener.ServiceStateChangedListener}.
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
*
* At registration, and when a specified telephony state changes, the telephony manager invokes
- * the appropriate callback method on the listener object and passes the current (updated)
+ * the appropriate callback method on the callback object and passes the current (updated)
* values.
* <p>
*
* If this TelephonyManager object has been created with {@link #createForSubscriptionId},
* applies to the given subId. Otherwise, applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
- * pass a separate listener object to each TelephonyManager object created with
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+ * subIds, pass a separate callback object to each TelephonyManager object created with
* {@link #createForSubscriptionId}.
*
* Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
*
- * This API should be used sparingly -- large numbers of listeners will cause system
- * instability. If a process has registered too many listeners without unregistering them, it
- * may encounter an {@link IllegalStateException} when trying to register more listeners.
+ * This API should be used sparingly -- large numbers of callbacks will cause system
+ * instability. If a process has registered too many callbacks without unregistering them, it
+ * may encounter an {@link IllegalStateException} when trying to register more callbacks.
*
* @param executor The executor of where the callback will execute.
- * @param listener The {@link PhoneStateListener} object to register.
+ * @param callback The {@link TelephonyCallback} object to register.
*/
- public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor,
- @NonNull PhoneStateListener listener) {
- if (executor == null || listener == null) {
- throw new IllegalArgumentException("PhoneStateListener and executor must be non-null");
+ public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull TelephonyCallback callback) {
+ if (executor == null || callback == null) {
+ throw new IllegalArgumentException("TelephonyCallback and executor must be non-null");
}
mTelephonyRegistryMgr = (TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (mTelephonyRegistryMgr != null) {
- mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId,
- getOpPackageName(), getAttributionTag(), listener, getITelephony() != null);
+ mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(),
+ getAttributionTag(), callback, getITelephony() != null);
} else {
throw new IllegalStateException("telephony service is null.");
}
}
/**
- * Unregister an existing {@link PhoneStateListener}.
+ * Unregister an existing {@link TelephonyCallback}.
*
- * @param listener The {@link PhoneStateListener} object to unregister.
+ * @param callback The {@link TelephonyCallback} object to unregister.
*/
- public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) {
+ public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) {
if (mContext == null) {
throw new IllegalStateException("telephony service is null.");
}
+ if (callback.callback == null) {
+ return;
+ }
+
mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
if (mTelephonyRegistryMgr != null) {
- mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(),
- getAttributionTag(), listener, getITelephony() != null);
+ mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(),
+ getAttributionTag(), callback, getITelephony() != null);
} else {
throw new IllegalStateException("telephony service is null.");
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 6fda2e12fffd..0ab679f79b29 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -866,6 +866,19 @@ public class ProvisioningManager {
public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67;
/**
+ * An integer key representing the voice over IMS opt-in provisioning status for the
+ * associated subscription. Determines whether the user can see for voice services over
+ * IMS.
+ * <p>
+ * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoIMS provisioning and
+ * {@link #PROVISIONING_VALUE_DISABLED} to disable VoIMS provisioning.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
+ */
+ public static final int KEY_VOIMS_OPT_IN_STATUS = 68;
+
+ /**
* Callback for IMS provisioning changes.
*/
public static class Callback {
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/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 9cfa640fce18..ad6d73c39962 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -19,6 +19,7 @@ package android.telephony.ims;
import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Build;
import android.os.Parcel;
@@ -46,6 +47,8 @@ public final class SipMessage implements Parcelable {
private final String mStartLine;
private final String mHeaderSection;
private final byte[] mContent;
+ private final String mViaBranchParam;
+ private final String mCallIdParam;
/**
* Represents a partially encoded SIP message.
@@ -63,6 +66,9 @@ public final class SipMessage implements Parcelable {
mStartLine = startLine;
mHeaderSection = headerSection;
mContent = content;
+
+ mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection);
+ mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection);
}
/**
@@ -73,6 +79,8 @@ public final class SipMessage implements Parcelable {
mHeaderSection = source.readString();
mContent = new byte[source.readInt()];
source.readByteArray(mContent);
+ mViaBranchParam = source.readString();
+ mCallIdParam = source.readString();
}
/**
@@ -97,6 +105,25 @@ public final class SipMessage implements Parcelable {
return mContent;
}
+ /**
+ * @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section
+ * 20.42 for more information on the Via header. If {@code null}, then there was either no
+ * Via parameter found in this SIP message's headers or no branch parameter found in the
+ * Via header.
+ */
+ public @Nullable String getViaBranchParameter() {
+ return mViaBranchParam;
+ }
+
+ /**
+ * @return the value associated with the call-id header of this SIP message. See RFC 3261
+ * section 20.8 for more information on the call-id header. If {@code null}, then there was no
+ * call-id header found in this SIP message's headers.
+ */
+ public @Nullable String getCallIdParameter() {
+ return mCallIdParam;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -108,6 +135,8 @@ public final class SipMessage implements Parcelable {
dest.writeString(mHeaderSection);
dest.writeInt(mContent.length);
dest.writeByteArray(mContent);
+ dest.writeString(mViaBranchParam);
+ dest.writeString(mCallIdParam);
}
public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 5eee3890f1dc..1b5e5603ec66 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -47,4 +47,6 @@ interface IImsConfig {
void removeRcsConfigCallback(IRcsConfigCallback c);
void triggerRcsReconfiguration();
void setRcsClientConfiguration(in RcsClientConfiguration rcc);
+ void notifyIntImsConfigChanged(int item, int value);
+ void notifyStringImsConfigChanged(int item, String value);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 9d919015087d..739946be2e5b 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -31,8 +31,6 @@ import android.telephony.ims.stub.SipDelegate;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.telephony.SipMessageParsingUtils;
-
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -188,7 +186,7 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe
}
private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
- String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+ String transactionId = m.getViaBranchParameter();
if (TextUtils.isEmpty(transactionId)) {
Log.w(LOG_TAG, "failure to parse SipMessage.");
throw new IllegalArgumentException("Malformed SipMessage, can not determine "
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index c877aca8ba96..3cd27264295c 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -32,8 +32,6 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import com.android.internal.telephony.SipMessageParsingUtils;
-
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.Executor;
@@ -268,7 +266,7 @@ public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection,
}
private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
- String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection());
+ String transactionId = m.getViaBranchParameter();
if (TextUtils.isEmpty(transactionId)) {
Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
+ "transaction ID.");
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 34984e05e181..21aeb64bb417 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -258,6 +258,16 @@ public class ImsConfigImplBase {
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
getImsConfigImpl().setRcsClientConfiguration(rcc);
}
+
+ @Override
+ public void notifyIntImsConfigChanged(int item, int value) throws RemoteException {
+ notifyImsConfigChanged(item, value);
+ }
+
+ @Override
+ public void notifyStringImsConfigChanged(int item, String value) throws RemoteException {
+ notifyImsConfigChanged(item, value);
+ }
}
/**
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/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e87f3d9aec76..7e04baa5f82b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1200,15 +1200,6 @@ interface ITelephony {
void shutdownMobileRadios();
/**
- * Set phone radio type and access technology.
- *
- * @param rafs an RadioAccessFamily array to indicate all phone's
- * new radio access family. The length of RadioAccessFamily
- * must equ]]al to phone count.
- */
- void setRadioCapability(in RadioAccessFamily[] rafs);
-
- /**
* Get phone radio type and access technology.
*
* @param phoneId which phone you want to get
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 386dafc590af..b61310aa4bd8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -102,7 +102,7 @@ class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
- @Presubmit
+ @FlakyTest
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 62ccb1a0b9db..6bf44920ee26 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -771,6 +771,24 @@
</intent-filter>
</activity>
+ <activity android:name="StretchShaderActivity"
+ android:label="RenderEffect/Stretch"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="EdgeEffectStretchActivity"
+ android:label="RenderEffect/EdgeEffect stretch"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="TextActivity"
android:label="Text/Simple Text"
android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/res/layout/stretch_layout.xml b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
new file mode 100644
index 000000000000..df5f297da729
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scroll_view"
+ android:edgeEffectType="stretch"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <HorizontalScrollView
+ android:id="@+id/horizontal_scroll_view"
+ android:edgeEffectType="stretch"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ </LinearLayout>
+ </HorizontalScrollView>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:src="@drawable/sunset1"/>
+
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
new file mode 100644
index 000000000000..f0e6299f09e9
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.HorizontalScrollView;
+import android.widget.ScrollView;
+
+public class EdgeEffectStretchActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.stretch_layout);
+ HorizontalScrollView hsv = findViewById(R.id.horizontal_scroll_view);
+ hsv.setStretchDistance(50f);
+
+ ScrollView sv = findViewById(R.id.scroll_view);
+ sv.setStretchDistance(50f);
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
new file mode 100644
index 000000000000..9bd933afa07d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -0,0 +1,540 @@
+/*
+ * 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.hwui;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.RuntimeShader;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class StretchShaderActivity extends Activity {
+
+ private static final float MAX_STRETCH_INTENSITY = 1.5f;
+ private static final float STRETCH_AFFECTED_DISTANCE = 1.0f;
+
+ private float mScrollX = 0f;
+ private float mScrollY = 0f;
+
+ private float mMaxStretchIntensity = MAX_STRETCH_INTENSITY;
+ private float mStretchAffectedDistance = STRETCH_AFFECTED_DISTANCE;
+
+ private float mOverscrollX = 25f;
+ private float mOverscrollY = 25f;
+
+ private RuntimeShader mRuntimeShader;
+ private ImageView mImageView;
+ private ImageView mTestImageView;
+
+ private Bitmap mBitmap;
+
+ private StretchDrawable mStretchDrawable = new StretchDrawable();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ LinearLayout linearLayout = new LinearLayout(this);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+
+ mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap();
+ mRuntimeShader = new RuntimeShader(SKSL, false);
+
+ BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP);
+ mRuntimeShader.setInputShader("uContentTexture", bitmapShader);
+
+ mImageView = new ImageView(this);
+
+ mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
+ mImageView.setImageDrawable(new ColorDrawable(Color.CYAN));
+
+ TextView overscrollXText = new TextView(this);
+ overscrollXText.setText("Overscroll X");
+
+ SeekBar overscrollXBar = new SeekBar(this);
+ overscrollXBar.setProgress(0);
+ overscrollXBar.setMin(-50);
+ overscrollXBar.setMax(50);
+ overscrollXBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mOverscrollX = progress;
+ overscrollXText.setText("Overscroll X: " + mOverscrollX);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ TextView overscrollYText = new TextView(this);
+ overscrollYText.setText("Overscroll Y");
+
+ SeekBar overscrollYBar = new SeekBar(this);
+ overscrollYBar.setProgress(0);
+ overscrollYBar.setMin(-50);
+ overscrollYBar.setMax(50);
+ overscrollYBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mOverscrollY = progress;
+ overscrollYText.setText("Overscroll Y: " + mOverscrollY);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ TextView scrollXText = new TextView(this);
+ scrollXText.setText("Scroll X");
+ SeekBar scrollXSeekBar = new SeekBar(this);
+ scrollXSeekBar.setMin(0);
+ scrollXSeekBar.setMax(100);
+ scrollXSeekBar.setProgress(0);
+ scrollXSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mScrollX = (progress / 100f);
+ scrollXText.setText("Scroll X: " + mScrollY);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ TextView scrollYText = new TextView(this);
+ scrollYText.setText("Scroll Y");
+ SeekBar scrollYSeekBar = new SeekBar(this);
+ scrollYSeekBar.setMin(0);
+ scrollYSeekBar.setMax(100);
+ scrollYSeekBar.setProgress(0);
+ scrollYSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mScrollY = (progress / 100f);
+ scrollYText.setText("Scroll Y: " + mScrollY);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ TextView stretchIntensityText = new TextView(this);
+ int stretchProgress = (int) (mMaxStretchIntensity * 100);
+ stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
+ SeekBar stretchIntensitySeekbar = new SeekBar(this);
+ stretchIntensitySeekbar.setProgress(stretchProgress);
+ stretchIntensitySeekbar.setMin(1);
+ stretchIntensitySeekbar.setMax((int) (MAX_STRETCH_INTENSITY * 100));
+ stretchIntensitySeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mMaxStretchIntensity = progress / 100f;
+ stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ TextView stretchDistanceText = new TextView(this);
+ stretchDistanceText.setText("StretchDistance");
+ SeekBar stretchDistanceSeekbar = new SeekBar(this);
+ stretchDistanceSeekbar.setMin(0);
+ stretchDistanceSeekbar.setProgress((int) (mStretchAffectedDistance * 100));
+ stretchDistanceSeekbar.setMax(100);
+ stretchDistanceSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mStretchAffectedDistance = progress / 100f;
+ stretchDistanceText.setText("StretchDistance: " + mStretchAffectedDistance);
+ updateShader();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+
+ linearLayout.addView(mImageView,
+ new LinearLayout.LayoutParams(
+ mBitmap.getWidth(),
+ mBitmap.getHeight())
+ );
+
+ linearLayout.addView(overscrollXText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+ linearLayout.addView(overscrollXBar,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ );
+
+ linearLayout.addView(overscrollYText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+ linearLayout.addView(overscrollYBar,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ );
+
+ linearLayout.addView(scrollXText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+
+ linearLayout.addView(scrollXSeekBar,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+
+ linearLayout.addView(scrollYText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+
+ linearLayout.addView(scrollYSeekBar,
+ new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+
+ linearLayout.addView(stretchIntensityText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ )
+ );
+
+ linearLayout.addView(stretchIntensitySeekbar,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ )
+ );
+
+ linearLayout.addView(stretchDistanceText,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+
+ linearLayout.addView(stretchDistanceSeekbar,
+ new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ ));
+
+ ImageView test = new ImageView(this);
+ mStretchDrawable.setBitmap(mBitmap);
+ test.setImageDrawable(mStretchDrawable);
+
+ mTestImageView = test;
+ linearLayout.addView(test,
+ new LinearLayout.LayoutParams(mBitmap.getWidth(), mBitmap.getHeight()));
+
+ setContentView(linearLayout);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mImageView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ updateShader();
+ mImageView.getViewTreeObserver().removeOnPreDrawListener(this);
+ return false;
+ }
+ });
+ }
+
+ private void updateShader() {
+ final float width = mImageView.getWidth();
+ final float height = mImageView.getHeight();
+ final float distanceNotStretched = mStretchAffectedDistance;
+ final float normOverScrollDistX = mOverscrollX / width;
+ final float normOverScrollDistY = mOverscrollY / height;
+ final float distanceStretchedX =
+ mStretchAffectedDistance
+ / (1 + Math.abs(normOverScrollDistX) * mMaxStretchIntensity);
+ final float distanceStretchedY =
+ mStretchAffectedDistance
+ / (1 + Math.abs(normOverScrollDistY) * mMaxStretchIntensity);
+ final float diffX = distanceStretchedX - distanceNotStretched;
+ final float diffY = distanceStretchedY - distanceNotStretched;
+ float uScrollX = mScrollX;
+ float uScrollY = mScrollY;
+
+ mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity);
+ mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance);
+ mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX);
+ mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY);
+ mRuntimeShader.setUniform("uDistDiffX", diffX);
+ mRuntimeShader.setUniform("uDistDiffY", diffY);
+ mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX);
+ mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY);
+ mRuntimeShader.setUniform("uScrollX", uScrollX);
+ mRuntimeShader.setUniform("uScrollY", uScrollY);
+ mRuntimeShader.setUniform("viewportWidth", width);
+ mRuntimeShader.setUniform("viewportHeight", height);
+
+ mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
+
+ mStretchDrawable.setStretchDistance(mStretchAffectedDistance);
+ mStretchDrawable.setOverscrollX(normOverScrollDistX);
+ mStretchDrawable.setOverscrollY(normOverScrollDistY);
+ }
+
+ private static class StretchDrawable extends Drawable {
+
+ private float mStretchDistance = 0;
+ private float mOverScrollX = 0f;
+ private float mOverScrollY = 0f;
+ private Bitmap mBitmap = null;
+
+ public void setStretchDistance(float stretchDistance) {
+ mStretchDistance = stretchDistance;
+ invalidateSelf();
+ }
+
+ public void setOverscrollX(float overscrollX) {
+ mOverScrollX = overscrollX;
+ invalidateSelf();
+ }
+
+ public void setOverscrollY(float overscrollY) {
+ mOverScrollY = overscrollY;
+ invalidateSelf();
+ }
+
+ public void setBitmap(Bitmap bitmap) {
+ mBitmap = bitmap;
+ invalidateSelf();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mStretchDistance > 0 && canvas instanceof RecordingCanvas) {
+ Rect bounds = getBounds();
+ ((RecordingCanvas) canvas).mNode.stretch(
+ 0,
+ 0,
+ bounds.width(),
+ bounds.height(),
+ mOverScrollX,
+ mOverScrollY,
+ mStretchDistance
+ );
+ }
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, 0f, 0f, null);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
+
+ private static final String SKSL = "in shader uContentTexture;\n"
+ + "uniform float uMaxStretchIntensity; // multiplier to apply to scale effect\n"
+ + "uniform float uStretchAffectedDist; // Maximum percentage to stretch beyond bounds"
+ + " of target\n"
+ + "\n"
+ + "// Distance stretched as a function of the normalized overscroll times scale "
+ + "intensity\n"
+ + "uniform float uDistanceStretchedX;\n"
+ + "uniform float uDistanceStretchedY;\n"
+ + "uniform float uDistDiffX;\n"
+ + "uniform float uDistDiffY; // Difference between the peak stretch amount and "
+ + "overscroll amount normalized\n"
+ + "uniform float uScrollX; // Horizontal offset represented as a ratio of pixels "
+ + "divided by the target width\n"
+ + "uniform float uScrollY; // Vertical offset represented as a ratio of pixels "
+ + "divided by the target height\n"
+ + "uniform float uOverscrollX; // Normalized overscroll amount in the horizontal "
+ + "direction\n"
+ + "uniform float uOverscrollY; // Normalized overscroll amount in the vertical "
+ + "direction\n"
+ + "\n"
+ + "uniform float viewportWidth; // target height in pixels\n"
+ + "uniform float viewportHeight; // target width in pixels\n"
+ + "\n"
+ + "vec4 main(vec2 coord) {\n"
+ + "\n"
+ + " // Normalize SKSL pixel coordinate into a unit vector\n"
+ + " vec2 uv = vec2(coord.x / viewportWidth, coord.y / viewportHeight);\n"
+ + " float inU = uv.x;\n"
+ + " float inV = uv.y;\n"
+ + " float outU;\n"
+ + " float outV;\n"
+ + " float stretchIntensity;\n"
+ + "\n"
+ + " // Add the normalized scroll position within scrolling list\n"
+ + " inU += uScrollX;\n"
+ + " inV += uScrollY;\n"
+ + "\n"
+ + " outU = inU;\n"
+ + " outV = inV;\n"
+ + " if (uOverscrollX > 0) {\n"
+ + " if (inU <= uStretchAffectedDist) {\n"
+ + " inU = uStretchAffectedDist - inU;\n"
+ + " float posBasedVariation = smoothstep(0., uStretchAffectedDist, inU);\n"
+ + " stretchIntensity = uMaxStretchIntensity * uOverscrollX * "
+ + "posBasedVariation;\n"
+ + " outU = uDistanceStretchedX - (inU / (1. + stretchIntensity));\n"
+ + " } else {\n"
+ + " outU = uDistDiffX + inU;\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " if (uOverscrollX < 0) {\n"
+ + " float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
+ + " if (inU >= stretchAffectedDist) {\n"
+ + " inU = inU - stretchAffectedDist;\n"
+ + " float posBasedVariation = (smoothstep(0., uStretchAffectedDist, "
+ + "inU));\n"
+ + " stretchIntensity = uMaxStretchIntensity * (-uOverscrollX) * "
+ + "posBasedVariation;\n"
+ + " outU = 1 - (uDistanceStretchedX - (inU / (1. + stretchIntensity)))"
+ + ";\n"
+ + " } else if (inU < stretchAffectedDist) {\n"
+ + " outU = -uDistDiffX + inU;\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " if (uOverscrollY > 0) {\n"
+ + " if (inV <= uStretchAffectedDist) {\n"
+ + " inV = uStretchAffectedDist - inV;\n"
+ + " float posBasedVariation = smoothstep(0., uStretchAffectedDist, inV);\n"
+ + " stretchIntensity = uMaxStretchIntensity * uOverscrollY * "
+ + "posBasedVariation;\n"
+ + " outV = uDistanceStretchedY - (inV / (1. + stretchIntensity));\n"
+ + " } else if (inV >= uStretchAffectedDist) {\n"
+ + " outV = uDistDiffY + inV;\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " if (uOverscrollY < 0) {\n"
+ + " float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
+ + " if (inV >= stretchAffectedDist) {\n"
+ + " inV = inV - stretchAffectedDist;\n"
+ + " float posBasedVariation = (smoothstep(0., uStretchAffectedDist, inV));\n"
+ + " stretchIntensity = uMaxStretchIntensity * (-uOverscrollY) * "
+ + "posBasedVariation;\n"
+ + " outV = 1 - (uDistanceStretchedY - (inV / (1. + stretchIntensity)));\n"
+ + " } else if (inV < stretchAffectedDist) {\n"
+ + " outV = -uDistDiffY + inV;\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " uv.x = outU;\n"
+ + " uv.y = outV;\n"
+ + " coord.x = uv.x * viewportWidth;\n"
+ + " coord.y = uv.y * viewportHeight;\n"
+ + " return sample(uContentTexture, coord);\n"
+ + "}";
+}
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/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index ee24d48f0ed5..d4f1ad317d31 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -26,13 +26,9 @@ java_test_host {
srcs: ["src/**/*.java"],
libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
static_libs: [
- "block_device_writer_jar",
"frameworks-base-hostutils",
],
test_suites: ["general-tests", "vts"],
- target_required: [
- "block_device_writer_module",
- ],
data: [
":NotoColorEmojiTtf",
":UpdatableSystemFontTestCertDer",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 7b919bd4b114..efe5d703880c 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -21,7 +21,6 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
<option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
<option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index e249f8a99b0c..92fa498f8326 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertWithMessage;
import android.platform.test.annotations.RootPermissionTest;
-import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
@@ -144,30 +143,6 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test {
assertThat(fontPathAfterReboot).isEqualTo(fontPath);
}
- @Test
- public void reboot_clearDamagedFiles() throws Exception {
- expectRemoteCommandToSucceed(String.format("cmd font update %s %s",
- TEST_NOTO_COLOR_EMOJI_V1_TTF, TEST_NOTO_COLOR_EMOJI_V1_TTF_FSV_SIG));
- String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF);
- assertThat(fontPath).startsWith("/data/fonts/files/");
- assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isTrue();
-
- BlockDeviceWriter.damageFileAgainstBlockDevice(getDevice(), fontPath, 0);
- expectRemoteCommandToSucceed("stop");
- // We have to make sure system_server is gone before dropping caches, because system_server
- // process holds font memory maps and prevents cache eviction.
- waitUntilSystemServerIsGone();
- BlockDeviceWriter.assertFileNotOpen(getDevice(), fontPath);
- BlockDeviceWriter.dropCaches(getDevice());
- assertThat(BlockDeviceWriter.canReadByte(getDevice(), fontPath, 0)).isFalse();
-
- expectRemoteCommandToSucceed("start");
- waitUntilFontCommandIsReady();
- String fontPathAfterReboot = getFontPath(NOTO_COLOR_EMOJI_TTF);
- assertWithMessage("Damaged file should be deleted")
- .that(fontPathAfterReboot).startsWith("/system");
- }
-
private String getFontPath(String fontFileName) throws Exception {
// TODO: add a dedicated command for testing.
String lines = expectRemoteCommandToSucceed("cmd font dump");
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index 7a60cc105a26..15d3398d43c0 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -24,8 +24,8 @@ 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.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Rule;
@@ -55,7 +55,7 @@ public class CaptivePortalTest {
mCode = request;
}
- @Override
+ // This is only @Override on R-
public void logEvent(int eventId, String packageName) throws RemoteException {
mCode = eventId;
mPackageName = packageName;
@@ -98,12 +98,24 @@ public class CaptivePortalTest {
assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
@Test
public void testLogEvent() {
+ /**
+ * From S testLogEvent is expected to do nothing but shouldn't crash (the API
+ * logEvent has been deprecated).
+ */
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);
+ }
+
+ @IgnoreAfter(Build.VERSION_CODES.R)
+ @Test
+ public void testLogEvent_UntilR() {
+ final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
+ 42, TEST_PACKAGE_NAME));
+ assertEquals(result.mCode, 42);
assertEquals(result.mPackageName, TEST_PACKAGE_NAME);
}
}
diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt
index 56b56efd501b..0ca4d9551f39 100644
--- a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt
+++ b/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt
@@ -63,10 +63,10 @@ class NetworkStateSnapshotTest {
@Test
fun testParcelUnparcel() {
- val emptySnapshot = NetworkStateSnapshot(LinkProperties(), NetworkCapabilities(),
- Network(TEST_NETID), null, TYPE_NONE)
+ val emptySnapshot = NetworkStateSnapshot(Network(TEST_NETID), NetworkCapabilities(),
+ LinkProperties(), null, TYPE_NONE)
val snapshot = NetworkStateSnapshot(
- TEST_LINK_PROPERTIES, TEST_CAPABILITIES, Network(TEST_NETID), TEST_IMSI, TYPE_WIFI)
+ Network(TEST_NETID), TEST_CAPABILITIES, TEST_LINK_PROPERTIES, TEST_IMSI, TYPE_WIFI)
assertParcelSane(emptySnapshot, 5)
assertParcelSane(snapshot, 5)
}
diff --git a/tests/net/common/java/android/net/UidRangeTest.java b/tests/net/common/java/android/net/UidRangeTest.java
new file mode 100644
index 000000000000..1b1c95431d6f
--- /dev/null
+++ b/tests/net/common/java/android/net/UidRangeTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.os.UserHandle.MIN_SECONDARY_USER_ID;
+import static android.os.UserHandle.SYSTEM;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.os.UserHandle.getUid;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Build;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UidRangeTest {
+
+ /*
+ * UidRange is no longer passed to netd. UID ranges between the framework and netd are passed as
+ * UidRangeParcel objects.
+ */
+
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
+ @Test
+ public void testSingleItemUidRangeAllowed() {
+ new UidRange(123, 123);
+ new UidRange(0, 0);
+ new UidRange(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testNegativeUidsDisallowed() {
+ try {
+ new UidRange(-2, 100);
+ fail("Exception not thrown for negative start UID");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new UidRange(-200, -100);
+ fail("Exception not thrown for negative stop UID");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testStopLessThanStartDisallowed() {
+ final int x = 4195000;
+ try {
+ new UidRange(x, x - 1);
+ fail("Exception not thrown for negative-length UID range");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testGetStartAndEndUser() throws Exception {
+ final UidRange uidRangeOfPrimaryUser = new UidRange(
+ getUid(USER_SYSTEM, 10000), getUid(USER_SYSTEM, 10100));
+ final UidRange uidRangeOfSecondaryUser = new UidRange(
+ getUid(MIN_SECONDARY_USER_ID, 10000), getUid(MIN_SECONDARY_USER_ID, 10100));
+ assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser());
+ assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser());
+ assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getStartUser());
+ assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser());
+
+ final UidRange uidRangeForDifferentUsers = new UidRange(
+ getUid(USER_SYSTEM, 10000), getUid(MIN_SECONDARY_USER_ID, 10100));
+ assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser());
+ assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testCreateForUser() throws Exception {
+ final UidRange uidRangeOfPrimaryUser = UidRange.createForUser(SYSTEM);
+ final UidRange uidRangeOfSecondaryUser = UidRange.createForUser(
+ UserHandle.of(USER_SYSTEM + 1));
+ assertTrue(uidRangeOfPrimaryUser.stop < uidRangeOfSecondaryUser.start);
+ assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser());
+ assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser());
+ assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getStartUser());
+ assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getEndUser());
+ }
+}
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 9ed55f098a16..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, netManager, 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/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 2e1c29a2e405..3a8d6004f66f 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -129,6 +129,7 @@ public class IpSecAlgorithmTest {
checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
}
checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
+ checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_CMAC, 128, 96);
checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 27224c216db3..64b774cc4340 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -20,14 +20,13 @@ import android.content.Context
import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.ConnectivityManager.TYPE_WIFI
import android.net.NetworkIdentity.SUBTYPE_COMBINED
-import android.net.NetworkIdentity.OEM_NONE;
-import android.net.NetworkIdentity.OEM_PAID;
-import android.net.NetworkIdentity.OEM_PRIVATE;
+import android.net.NetworkIdentity.OEM_NONE
+import android.net.NetworkIdentity.OEM_PAID
+import android.net.NetworkIdentity.OEM_PRIVATE
import android.net.NetworkIdentity.buildNetworkIdentity
import android.net.NetworkStats.DEFAULT_NETWORK_ALL
import android.net.NetworkStats.METERED_ALL
import android.net.NetworkStats.ROAMING_ALL
-import android.net.NetworkTemplate.MATCH_ETHERNET
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
import android.net.NetworkTemplate.MATCH_WIFI
@@ -50,7 +49,6 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
-import kotlin.test.fail
private const val TEST_IMSI1 = "imsi1"
private const val TEST_IMSI2 = "imsi2"
@@ -60,17 +58,17 @@ private const val TEST_SSID1 = "ssid1"
class NetworkTemplateTest {
private val mockContext = mock(Context::class.java)
- private fun buildMobileNetworkState(subscriberId: String): NetworkState =
+ private fun buildMobileNetworkState(subscriberId: String): NetworkStateSnapshot =
buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId)
- private fun buildWifiNetworkState(ssid: String): NetworkState =
+ private fun buildWifiNetworkState(ssid: String): NetworkStateSnapshot =
buildNetworkState(TYPE_WIFI, ssid = ssid)
private fun buildNetworkState(
type: Int,
subscriberId: String? = null,
ssid: String? = null,
- oemManaged: Int = OEM_NONE,
- ): NetworkState {
+ oemManaged: Int = OEM_NONE
+ ): NetworkStateSnapshot {
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
@@ -81,7 +79,7 @@ class NetworkTemplateTest {
setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
(oemManaged and OEM_PRIVATE) == OEM_PRIVATE)
}
- return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId)
+ return NetworkStateSnapshot(mock(Network::class.java), caps, lp, subscriberId, type)
}
private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
@@ -179,7 +177,7 @@ class NetworkTemplateTest {
OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE)
// Verify that "not OEM managed network" constants are equal.
- assertEquals(OEM_MANAGED_NO, OEM_NONE);
+ assertEquals(OEM_MANAGED_NO, OEM_NONE)
// Verify the constants don't conflict.
assertEquals(constantValues.size, constantValues.distinct().count())
@@ -201,8 +199,13 @@ class NetworkTemplateTest {
* @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide
* one of {@code TEST_SSID*}.
*/
- private fun matchOemManagedIdent(networkType: Int, matchType:Int, subscriberId: String? = null,
- templateSsid: String? = null, identSsid: String? = null) {
+ private fun matchOemManagedIdent(
+ networkType: Int,
+ matchType: Int,
+ subscriberId: String? = null,
+ templateSsid: String? = null,
+ identSsid: String? = null
+ ) {
val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE)
// A null subscriberId needs a null matchSubscriberIds argument as well.
val matchSubscriberIds = if (subscriberId == null) null else arrayOf(subscriberId)
diff --git a/tests/net/java/android/net/UidRangeTest.java b/tests/net/java/android/net/UidRangeTest.java
deleted file mode 100644
index ea1df096e208..000000000000
--- a/tests/net/java/android/net/UidRangeTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import static org.junit.Assert.fail;
-
-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 UidRangeTest {
-
- /*
- * UidRange is no longer passed to netd. UID ranges between the framework and netd are passed as
- * UidRangeParcel objects.
- */
-
- @Test
- public void testSingleItemUidRangeAllowed() {
- new UidRange(123, 123);
- new UidRange(0, 0);
- new UidRange(Integer.MAX_VALUE, Integer.MAX_VALUE);
- }
-
- @Test
- public void testNegativeUidsDisallowed() {
- try {
- new UidRange(-2, 100);
- fail("Exception not thrown for negative start UID");
- } catch (IllegalArgumentException expected) {
- }
-
- try {
- new UidRange(-200, -100);
- fail("Exception not thrown for negative stop UID");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testStopLessThanStartDisallowed() {
- final int x = 4195000;
- try {
- new UidRange(x, x - 1);
- fail("Exception not thrown for negative-length UID range");
- } catch (IllegalArgumentException expected) {
- }
- }
-} \ No newline at end of file
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index 9b0cfa9db30f..c1315f64c56b 100644
--- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -21,7 +21,7 @@ import android.content.res.Resources
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
-import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener
+import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
import android.provider.Settings
import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
@@ -120,9 +120,9 @@ class MultinetworkPolicyTrackerTest {
MULTIPATH_PREFERENCE_PERFORMANCE.toString())
val listenerCaptor = ArgumentCaptor.forClass(
- ActiveDataSubscriptionIdChangedListener::class.java)
+ ActiveDataSubscriptionIdListener::class.java)
verify(telephonyManager, times(1))
- .registerPhoneStateListener(any(), listenerCaptor.capture())
+ .registerTelephonyCallback(any(), listenerCaptor.capture())
val listener = listenerCaptor.value
listener.onActiveDataSubscriptionIdChanged(testSubId)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1cfc3f9f9e5c..d72517147e4d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -91,6 +91,10 @@ import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_REMOVED;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
@@ -99,6 +103,7 @@ import static com.android.testutils.ConcurrentUtils.await;
import static com.android.testutils.ConcurrentUtils.durationOf;
import static com.android.testutils.ExceptionUtils.ignoreExceptions;
import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor;
+import static com.android.testutils.MiscAsserts.assertContainsAll;
import static com.android.testutils.MiscAsserts.assertContainsExactly;
import static com.android.testutils.MiscAsserts.assertEmpty;
import static com.android.testutils.MiscAsserts.assertLength;
@@ -145,6 +150,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;
@@ -176,7 +182,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;
@@ -199,7 +204,7 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
@@ -218,6 +223,8 @@ import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnTransportInfo;
import android.net.metrics.IpConnectivityLog;
+import android.net.resolv.aidl.Nat64PrefixEventParcel;
+import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
@@ -244,7 +251,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;
@@ -276,6 +282,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;
@@ -345,6 +352,9 @@ public class ConnectivityServiceTest {
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
+ // Broadcasts can take a long time to be delivered. The test will not wait for that long unless
+ // there is a failure, so use a long timeout.
+ private static final int BROADCAST_TIMEOUT_MS = 30_000;
private static final int TEST_LINGER_DELAY_MS = 400;
private static final int TEST_NASCENT_DELAY_MS = 300;
// Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
@@ -407,6 +417,8 @@ public class ConnectivityServiceTest {
private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker;
private VpnManagerService mVpnManagerService;
+ private TestNetworkCallback mDefaultNetworkCallback;
+ private TestNetworkCallback mSystemDefaultNetworkCallback;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mUidRules = RULE_NONE;
@@ -414,7 +426,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;
@@ -431,7 +443,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 =
@@ -530,6 +542,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);
}
@@ -1072,9 +1085,11 @@ public class ConnectivityServiceTest {
}
}
- private Set<UidRange> uidRangesForUid(int uid) {
+ private Set<UidRange> uidRangesForUids(int... uids) {
final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
+ for (final int uid : uids) {
+ ranges.add(new UidRange(uid, uid));
+ }
return ranges;
}
@@ -1115,7 +1130,7 @@ public class ConnectivityServiceTest {
return mDeviceIdleInternal;
}
},
- mNetworkManagementService, mMockNetd, userId, mKeyStore);
+ mNetworkManagementService, mMockNetd, userId, mVpnProfileStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1204,13 +1219,13 @@ public class ConnectivityServiceTest {
public void establishForMyUid(LinkProperties lp) throws Exception {
final int uid = Process.myUid();
- establish(lp, uid, uidRangesForUid(uid), true, true, false);
+ establish(lp, uid, uidRangesForUids(uid), true, true, false);
}
public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
throws Exception {
final int uid = Process.myUid();
- establish(makeLinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+ establish(makeLinkProperties(), uid, uidRangesForUids(uid), validated, hasInternet,
isStrictMode);
}
@@ -1294,8 +1309,9 @@ public class ConnectivityServiceTest {
return mVMSHandlerThread;
}
- public KeyStore getKeyStore() {
- return mKeyStore;
+ @Override
+ public VpnProfileStore getVpnProfileStore() {
+ return mVpnProfileStore;
}
public INetd getNetd() {
@@ -1318,7 +1334,7 @@ public class ConnectivityServiceTest {
}
- private void processBroadcastForVpn(Intent intent) {
+ private void processBroadcast(Intent intent) {
mServiceContext.sendBroadcast(intent);
HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS);
waitForIdle();
@@ -1409,6 +1425,7 @@ public class ConnectivityServiceTest {
private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043);
private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "",
UserInfo.FLAG_PRIMARY);
+ private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER);
private static final int RESTRICTED_USER = 1;
private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "",
@@ -1426,6 +1443,8 @@ public class ConnectivityServiceTest {
MockitoAnnotations.initMocks(this);
when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO));
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE));
when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
// canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
// it was started from, i.e., PRIMARY_USER.
@@ -1462,8 +1481,6 @@ public class ConnectivityServiceTest {
mDeps = makeDependencies();
returnRealCallingUid();
mService = new ConnectivityService(mServiceContext,
- mNetworkManagementService,
- mStatsService,
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
@@ -1547,6 +1564,7 @@ public class ConnectivityServiceTest {
@After
public void tearDown() throws Exception {
+ unregisterDefaultNetworkCallbacks();
setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
@@ -1652,6 +1670,7 @@ public class ConnectivityServiceTest {
assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
// Test getAllNetworks()
assertEmpty(mCm.getAllNetworks());
+ assertEmpty(mCm.getAllNetworkStateSnapshot());
}
/**
@@ -1680,7 +1699,7 @@ public class ConnectivityServiceTest {
}
public Intent expectBroadcast() throws Exception {
- return expectBroadcast(TIMEOUT_MS);
+ return expectBroadcast(BROADCAST_TIMEOUT_MS);
}
public void expectNoBroadcast(int timeoutMs) throws Exception {
@@ -5478,18 +5497,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(NetworkState[].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];
@@ -5503,8 +5523,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
@@ -5524,47 +5545,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(NetworkState[].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();
@@ -5577,7 +5597,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.
@@ -5594,9 +5614,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
@@ -5606,22 +5626,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
@@ -5633,17 +5653,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(NetworkState[].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
@@ -5652,13 +5672,13 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.adjustScore(-40);
mEthernetNetworkAgent.connect(false);
waitForIdle();
- verify(mStatsService).forceUpdateIfaces(any(Network[].class),
- any(NetworkState[].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
@@ -5668,27 +5688,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
@@ -5921,6 +5941,16 @@ public class ConnectivityServiceTest {
assertEquals("strict.example.com", cbi.getLp().getPrivateDnsServerName());
}
+ private PrivateDnsValidationEventParcel makePrivateDnsValidationEvent(
+ final int netId, final String ipAddress, final String hostname, final int validation) {
+ final PrivateDnsValidationEventParcel event = new PrivateDnsValidationEventParcel();
+ event.netId = netId;
+ event.ipAddress = ipAddress;
+ event.hostname = hostname;
+ event.validation = validation;
+ return event;
+ }
+
@Test
public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception {
// The default on Android is opportunistic mode ("Automatic").
@@ -5951,8 +5981,9 @@ public class ConnectivityServiceTest {
// Send a validation event for a server that is not part of the current
// resolver config. The validation event should be ignored.
- mService.mNetdEventCallback.onPrivateDnsValidationEvent(
- mCellNetworkAgent.getNetwork().netId, "", "145.100.185.18", true);
+ mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent(
+ makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, "",
+ "145.100.185.18", VALIDATION_RESULT_SUCCESS));
cellNetworkCallback.assertNoCallback();
// Add a dns server to the LinkProperties.
@@ -5969,20 +6000,23 @@ public class ConnectivityServiceTest {
// Send a validation event containing a hostname that is not part of
// the current resolver config. The validation event should be ignored.
- mService.mNetdEventCallback.onPrivateDnsValidationEvent(
- mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "hostname", true);
+ mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent(
+ makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId,
+ "145.100.185.16", "hostname", VALIDATION_RESULT_SUCCESS));
cellNetworkCallback.assertNoCallback();
// Send a validation event where validation failed.
- mService.mNetdEventCallback.onPrivateDnsValidationEvent(
- mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", false);
+ mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent(
+ makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId,
+ "145.100.185.16", "", VALIDATION_RESULT_FAILURE));
cellNetworkCallback.assertNoCallback();
// Send a validation event where validation succeeded for a server in
// the current resolver config. A LinkProperties callback with updated
// private dns fields should be sent.
- mService.mNetdEventCallback.onPrivateDnsValidationEvent(
- mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
+ mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent(
+ makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId,
+ "145.100.185.16", "", VALIDATION_RESULT_SUCCESS));
cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
@@ -6361,7 +6395,7 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final Set<UidRange> ranges = uidRangesForUid(uid);
+ final Set<UidRange> ranges = uidRangesForUids(uid);
mMockVpn.registerAgent(ranges);
mMockVpn.setUnderlyingNetworks(new Network[0]);
@@ -6833,7 +6867,7 @@ public class ConnectivityServiceTest {
final int uid = Process.myUid();
NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertNotNull("nc=" + nc, nc.getUids());
- assertEquals(nc.getUids(), uidRangesForUid(uid));
+ assertEquals(nc.getUids(), uidRangesForUids(uid));
assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE);
// Set an underlying network and expect to see the VPN transports change.
@@ -6854,7 +6888,7 @@ public class ConnectivityServiceTest {
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
- processBroadcastForVpn(addedIntent);
+ processBroadcast(addedIntent);
// Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
// restricted user.
@@ -6879,7 +6913,7 @@ public class ConnectivityServiceTest {
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- processBroadcastForVpn(removedIntent);
+ processBroadcast(removedIntent);
// Expect that the VPN gains the UID range for the restricted user, and that the capability
// change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6937,7 +6971,7 @@ public class ConnectivityServiceTest {
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- processBroadcastForVpn(addedIntent);
+ processBroadcast(addedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6948,7 +6982,7 @@ public class ConnectivityServiceTest {
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- processBroadcastForVpn(removedIntent);
+ processBroadcast(removedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -7103,7 +7137,7 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+ mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUids(Process.myUid()),
new LinkProperties());
mMockVpn.connect(true);
waitForIdle();
@@ -7488,8 +7522,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";
@@ -7497,7 +7530,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 {
@@ -7543,7 +7576,7 @@ public class ConnectivityServiceTest {
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- processBroadcastForVpn(addedIntent);
+ processBroadcast(addedIntent);
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
@@ -7826,6 +7859,16 @@ public class ConnectivityServiceTest {
return stacked;
}
+ private Nat64PrefixEventParcel makeNat64PrefixEvent(final int netId, final int prefixOperation,
+ final String prefixAddress, final int prefixLength) {
+ final Nat64PrefixEventParcel event = new Nat64PrefixEventParcel();
+ event.netId = netId;
+ event.prefixOperation = prefixOperation;
+ event.prefixAddress = prefixAddress;
+ event.prefixLength = prefixLength;
+ return event;
+ }
+
@Test
public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -7856,7 +7899,6 @@ public class ConnectivityServiceTest {
cellLp.addRoute(defaultRoute);
cellLp.addRoute(ipv6Subnet);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
- reset(mNetworkManagementService);
reset(mMockDnsResolver);
reset(mMockNetd);
reset(mBatteryStatsService);
@@ -7896,7 +7938,6 @@ public class ConnectivityServiceTest {
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
- reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
@@ -7912,8 +7953,8 @@ public class ConnectivityServiceTest {
// When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
- mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
- kNat64PrefixString, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+ makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
LinkProperties lpBeforeClat = networkCallback.expectCallback(
CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
assertEquals(0, lpBeforeClat.getStackedLinks().size());
@@ -7953,8 +7994,8 @@ public class ConnectivityServiceTest {
.thenReturn(getClatInterfaceConfigParcel(myIpv4));
// Change the NAT64 prefix without first removing it.
// Expect clatd to be stopped and started with the new prefix.
- mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
- kOtherNat64PrefixString, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
+ cellNetId, PREFIX_OPERATION_ADDED, kOtherNat64PrefixString, 96));
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
@@ -7996,15 +8037,14 @@ public class ConnectivityServiceTest {
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
- reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfigParcel(myIpv4));
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
- mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
- kOtherNat64PrefixString, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
+ cellNetId, PREFIX_OPERATION_REMOVED, kOtherNat64PrefixString, 96));
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getNat64Prefix() == null);
@@ -8016,8 +8056,8 @@ public class ConnectivityServiceTest {
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
assertRoutesRemoved(cellNetId, ipv4Subnet); // Directly-connected routes auto-added.
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
- mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
- kNat64PrefixString, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
+ cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
@@ -8029,8 +8069,8 @@ public class ConnectivityServiceTest {
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME);
// NAT64 prefix is removed. Expect that clat is stopped.
- mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
- kNat64PrefixString, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
+ cellNetId, PREFIX_OPERATION_REMOVED, kNat64PrefixString, 96));
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null);
assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault);
@@ -8118,8 +8158,8 @@ public class ConnectivityServiceTest {
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
- mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
- pref64FromDnsStr, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+ makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
@@ -8152,8 +8192,8 @@ public class ConnectivityServiceTest {
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
// Stopping prefix discovery results in a prefix removed notification.
- mService.mNetdEventCallback.onNat64PrefixEvent(netId, false /* added */,
- pref64FromDnsStr, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+ makeNat64PrefixEvent(netId, PREFIX_OPERATION_REMOVED, pref64FromDnsStr, 96));
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
@@ -8191,8 +8231,8 @@ public class ConnectivityServiceTest {
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
- mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
- pref64FromDnsStr, 96);
+ mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
+ makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
@@ -8233,7 +8273,6 @@ public class ConnectivityServiceTest {
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellNetworkAgent.sendLinkProperties(cellLp);
- reset(mNetworkManagementService);
mCellNetworkAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(),
@@ -8927,8 +8966,8 @@ public class ConnectivityServiceTest {
ConnectivityManager.getNetworkTypeName(TYPE_MOBILE),
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
- nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
- 0, INVALID_UID, mQosCallbackTracker);
+ nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
+ INVALID_UID, mQosCallbackTracker);
}
@Test
@@ -9256,7 +9295,7 @@ public class ConnectivityServiceTest {
private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception {
final int uid = Process.myUid();
- assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid);
+ assertVpnUidRangesUpdated(add, uidRangesForUids(uid), uid);
}
private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
@@ -9467,6 +9506,10 @@ public class ConnectivityServiceTest {
fail("TOO_MANY_REQUESTS never thrown");
}
+ private UidRange createUidRange(int userId) {
+ return UidRange.createForUser(UserHandle.of(userId));
+ }
+
private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid)
throws Exception {
final ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -9641,7 +9684,7 @@ public class ConnectivityServiceTest {
}
@Test
- public void testOemNetworkRequestFactoryCorrectlySetsUids()
+ public void testOemNetworkRequestFactoryMultiplePrefsCorrectlySetsUids()
throws Exception {
// Arrange PackageManager mocks
final String testPackageName2 = "com.google.apps.dialer";
@@ -9672,6 +9715,46 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids()
+ throws Exception {
+ // Arrange users
+ final int secondUser = 10;
+ final UserHandle secondUserHandle = new UserHandle(secondUser);
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+
+ // Arrange PackageManager mocks
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+
+ // Build OemNetworkPreferences object
+ final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
+ .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref)
+ .build();
+
+ // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences()
+ final List<ConnectivityService.NetworkRequestInfo> nris =
+ new ArrayList<>(
+ mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(
+ pref));
+
+ // UIDs for all users and all managed packages should be present.
+ // Two users each with two packages.
+ final int expectedUidSize = 2;
+ final List<UidRange> uids =
+ new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids());
+ assertEquals(expectedUidSize, uids.size());
+
+ // Sort by uid to access nris by index
+ uids.sort(Comparator.comparingInt(uid -> uid.start));
+ final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+ assertEquals(TEST_PACKAGE_UID, uids.get(0).start);
+ assertEquals(TEST_PACKAGE_UID, uids.get(0).stop);
+ assertEquals(secondUserTestPackageUid, uids.get(1).start);
+ assertEquals(secondUserTestPackageUid, uids.get(1).stop);
+ }
+
+ @Test
public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference()
throws Exception {
// Expectations
@@ -9801,6 +9884,54 @@ public class ConnectivityServiceTest {
assertEquals(expectedPerAppNetwork, defaultNetwork);
assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size());
}
+ verifyMultipleDefaultCallbacks(expectedDefaultNetwork, expectedPerAppNetwork);
+ }
+
+ /**
+ * Verify default callbacks for 'available' fire as expected. This will only run if
+ * registerDefaultNetworkCallbacks() was executed prior and will only be different if the
+ * setOemNetworkPreference() per-app API was used for the current process.
+ * @param expectedSystemDefault the expected network for the system default.
+ * @param expectedPerAppDefault the expected network for the current process's default.
+ */
+ private void verifyMultipleDefaultCallbacks(
+ @NonNull final Network expectedSystemDefault,
+ @NonNull final Network expectedPerAppDefault) {
+ if (null != mSystemDefaultNetworkCallback && null != expectedSystemDefault
+ && mService.mNoServiceNetwork.network() != expectedSystemDefault) {
+ // getLastAvailableNetwork() is used as this method can be called successively with
+ // the same network to validate therefore expectAvailableThenValidatedCallbacks
+ // can't be used.
+ assertEquals(mSystemDefaultNetworkCallback.getLastAvailableNetwork(),
+ expectedSystemDefault);
+ }
+ if (null != mDefaultNetworkCallback && null != expectedPerAppDefault
+ && mService.mNoServiceNetwork.network() != expectedPerAppDefault) {
+ assertEquals(mDefaultNetworkCallback.getLastAvailableNetwork(),
+ expectedPerAppDefault);
+ }
+ }
+
+ private void registerDefaultNetworkCallbacks() {
+ // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback()
+ mServiceContext.setPermission(
+ Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+ mSystemDefaultNetworkCallback = new TestNetworkCallback();
+ mDefaultNetworkCallback = new TestNetworkCallback();
+ mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
+ new Handler(ConnectivityThread.getInstanceLooper()));
+ mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ mServiceContext.setPermission(
+ Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
+ }
+
+ private void unregisterDefaultNetworkCallbacks() {
+ if (null != mDefaultNetworkCallback) {
+ mCm.unregisterNetworkCallback(mDefaultNetworkCallback);
+ }
+ if (null != mSystemDefaultNetworkCallback) {
+ mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
+ }
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -9828,7 +9959,7 @@ public class ConnectivityServiceTest {
assertEquals(1, mService.mDefaultNetworkRequests.size());
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+ toUidRangeStableParcels(uidRangesForUids(testPackageUid));
setupSetOemNetworkPreferenceForPreferenceTest(
networkPrefToSetup, uidRanges, testPackageName);
}
@@ -9849,12 +9980,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 {
@@ -9884,6 +10014,7 @@ public class ConnectivityServiceTest {
@OemNetworkPreferences.OemNetworkPreference final int networkPref =
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
final int expectedOemPrefRequestSize = 1;
+ registerDefaultNetworkCallbacks();
// Setup the test process to use networkPref for their default network.
setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
@@ -9898,6 +10029,7 @@ public class ConnectivityServiceTest {
// Verify that the active network is correct
verifyActiveNetwork(TRANSPORT_ETHERNET);
+ // default NCs will be unregistered in tearDown
}
@Test
@@ -9905,6 +10037,7 @@ public class ConnectivityServiceTest {
@OemNetworkPreferences.OemNetworkPreference final int networkPref =
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
final int expectedOemPrefRequestSize = 1;
+ registerDefaultNetworkCallbacks();
// Setup the test process to use networkPref for their default network.
setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
@@ -9925,6 +10058,7 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.getNetwork());
assertFalse(mCm.isActiveNetworkMetered());
+ // default NCs will be unregistered in tearDown
}
@Test
@@ -10055,6 +10189,10 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(defaultNetworkCallback);
}
+ /**
+ * This method assumes that the same uidRanges input will be used to verify that dependencies
+ * are called as expected.
+ */
private void verifySetOemNetworkPreferenceForPreference(
@NonNull final UidRangeParcel[] uidRanges,
final int addUidRangesNetId,
@@ -10062,16 +10200,30 @@ public class ConnectivityServiceTest {
final int removeUidRangesNetId,
final int removeUidRangesTimes,
final boolean shouldDestroyNetwork) throws RemoteException {
+ verifySetOemNetworkPreferenceForPreference(uidRanges, uidRanges,
+ addUidRangesNetId, addUidRangesTimes, removeUidRangesNetId, removeUidRangesTimes,
+ shouldDestroyNetwork);
+ }
+
+ private void verifySetOemNetworkPreferenceForPreference(
+ @NonNull final UidRangeParcel[] addedUidRanges,
+ @NonNull final UidRangeParcel[] removedUidRanges,
+ final int addUidRangesNetId,
+ final int addUidRangesTimes,
+ final int removeUidRangesNetId,
+ final int removeUidRangesTimes,
+ final boolean shouldDestroyNetwork) throws RemoteException {
final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId;
final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId;
// Validate netd.
verify(mMockNetd, times(addUidRangesTimes))
.networkAddUidRanges(
- (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(uidRanges));
+ (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(addedUidRanges));
verify(mMockNetd, times(removeUidRangesTimes))
.networkRemoveUidRanges(
- (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), eq(uidRanges));
+ (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)),
+ eq(removedUidRanges));
if (shouldDestroyNetwork) {
verify(mMockNetd, times(1))
.networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)));
@@ -10081,7 +10233,6 @@ public class ConnectivityServiceTest {
/**
* Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
- * @throws Exception
*/
@Test
public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception {
@@ -10090,7 +10241,7 @@ public class ConnectivityServiceTest {
final int testPackageUid = 123;
final String testPackageName = "com.google.apps.contacts";
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageUid));
+ toUidRangeStableParcels(uidRangesForUids(testPackageUid));
// Validate the starting requests only includes the fallback request.
assertEquals(1, mService.mDefaultNetworkRequests.size());
@@ -10109,9 +10260,8 @@ public class ConnectivityServiceTest {
}
/**
- * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID following in order:
+ * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
* NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
- * @throws Exception
*/
@Test
public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly()
@@ -10120,9 +10270,8 @@ public class ConnectivityServiceTest {
OEM_NETWORK_PREFERENCE_OEM_PAID;
// Arrange PackageManager mocks
- final int testPackageNameUid = 123;
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
// Verify the starting state. No networks should be connected.
@@ -10177,9 +10326,8 @@ public class ConnectivityServiceTest {
}
/**
- * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK following in order:
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order:
* NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
- * @throws Exception
*/
@Test
public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly()
@@ -10188,9 +10336,8 @@ public class ConnectivityServiceTest {
OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
// Arrange PackageManager mocks
- final int testPackageNameUid = 123;
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
// Verify the starting state. This preference doesn't support using the fallback network
@@ -10240,10 +10387,9 @@ public class ConnectivityServiceTest {
}
/**
- * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY following in order:
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order:
* NET_CAPABILITY_OEM_PAID
* This preference should only apply to OEM_PAID networks.
- * @throws Exception
*/
@Test
public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly()
@@ -10252,9 +10398,8 @@ public class ConnectivityServiceTest {
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
// Arrange PackageManager mocks
- final int testPackageNameUid = 123;
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
// Verify the starting state. This preference doesn't support using the fallback network
@@ -10294,10 +10439,9 @@ public class ConnectivityServiceTest {
}
/**
- * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY following in order:
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order:
* NET_CAPABILITY_OEM_PRIVATE
* This preference should only apply to OEM_PRIVATE networks.
- * @throws Exception
*/
@Test
public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly()
@@ -10306,9 +10450,8 @@ public class ConnectivityServiceTest {
OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
// Arrange PackageManager mocks
- final int testPackageNameUid = 123;
final UidRangeParcel[] uidRanges =
- toUidRangeStableParcels(uidRangesForUid(testPackageNameUid));
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
// Verify the starting state. This preference doesn't support using the fallback network
@@ -10347,7 +10490,417 @@ public class ConnectivityServiceTest {
true /* shouldDestroyNetwork */);
}
- private UidRange createUidRange(int userId) {
- return UidRange.createForUser(UserHandle.of(userId));
+ @Test
+ public void testMultilayerForMultipleUsersEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+ // Arrange users
+ final int secondUser = 10;
+ final UserHandle secondUserHandle = new UserHandle(secondUser);
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+
+ // Arrange PackageManager mocks
+ final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(
+ uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. No networks should be connected.
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test that we correctly add the expected values for multiple users.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test that we correctly remove the expected values for multiple users.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifySetOemNetworkPreferenceForPreference(uidRanges,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 0 /* times */,
+ true /* shouldDestroyNetwork */);
+ }
+
+ @Test
+ public void testMultilayerForBroadcastedUsersEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+
+ // Arrange users
+ final int secondUser = 10;
+ final UserHandle secondUserHandle = new UserHandle(secondUser);
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE));
+
+ // Arrange PackageManager mocks
+ final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
+ final UidRangeParcel[] uidRangesSingleUser =
+ toUidRangeStableParcels(
+ uidRangesForUids(TEST_PACKAGE_UID));
+ final UidRangeParcel[] uidRangesBothUsers =
+ toUidRangeStableParcels(
+ uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
+ setupSetOemNetworkPreferenceForPreferenceTest(
+ networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME);
+
+ // Verify the starting state. No networks should be connected.
+ verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test that we correctly add the expected values for multiple users.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Send a broadcast indicating a user was added.
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle));
+ final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
+ processBroadcast(addedIntent);
+
+ // Test that we correctly add values for all users and remove for the single user.
+ verifySetOemNetworkPreferenceForPreference(uidRangesBothUsers, uidRangesSingleUser,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Send a broadcast indicating a user was removed.
+ when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
+ Arrays.asList(PRIMARY_USER_HANDLE));
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
+ processBroadcast(removedIntent);
+
+ // Test that we correctly add values for the single user and remove for the all users.
+ verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, uidRangesBothUsers,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+ }
+
+ /**
+ * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
+ * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
+ */
+ @Test
+ public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+ final int expectedDefaultRequestSize = 2;
+ final int expectedOemPrefRequestSize = 3;
+ registerDefaultNetworkCallbacks();
+
+ // The fallback as well as the OEM preference should now be tracked.
+ assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mCellNetworkAgent.getNetwork());
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mWiFiNetworkAgent.getNetwork(),
+ mWiFiNetworkAgent.getNetwork());
+
+ // Disconnecting unmetered Wi-Fi will put the pref on OEM_PAID and fallback on cellular.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting OEM_PAID will put both on null as it is the last network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ null);
+
+ // default NCs will be unregistered in tearDown
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order:
+ * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID
+ */
+ @Test
+ public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidNoFallbackCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+ final int expectedDefaultRequestSize = 2;
+ final int expectedOemPrefRequestSize = 2;
+ registerDefaultNetworkCallbacks();
+
+ // The fallback as well as the OEM preference should now be tracked.
+ assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network but not the pref.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mService.mNoServiceNetwork.network());
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mWiFiNetworkAgent.getNetwork(),
+ mWiFiNetworkAgent.getNetwork());
+
+ // Disconnecting unmetered Wi-Fi will put the OEM pref on OEM_PAID and fallback on cellular.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting OEM_PAID puts the fallback on null and the pref on the disconnected net.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mService.mNoServiceNetwork.network());
+
+ // default NCs will be unregistered in tearDown
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order:
+ * NET_CAPABILITY_OEM_PAID
+ * This preference should only apply to OEM_PAID networks.
+ */
+ @Test
+ public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidOnlyCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+ final int expectedDefaultRequestSize = 2;
+ final int expectedOemPrefRequestSize = 1;
+ registerDefaultNetworkCallbacks();
+
+ // The fallback as well as the OEM preference should now be tracked.
+ assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mService.mNoServiceNetwork.network());
+
+ // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mWiFiNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting OEM_PAID will keep the fallback on cellular and nothing for OEM_PAID.
+ // OEM_PAID_ONLY not supporting a fallback now uses the disconnected network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mService.mNoServiceNetwork.network());
+
+ // Disconnecting cellular will put the fallback on null and the pref on disconnected.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mService.mNoServiceNetwork.network());
+
+ // default NCs will be unregistered in tearDown
+ }
+
+ /**
+ * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order:
+ * NET_CAPABILITY_OEM_PRIVATE
+ * This preference should only apply to OEM_PRIVATE networks.
+ */
+ @Test
+ public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPrivateOnlyCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+ setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref);
+ final int expectedDefaultRequestSize = 2;
+ final int expectedOemPrefRequestSize = 1;
+ registerDefaultNetworkCallbacks();
+
+ // The fallback as well as the OEM preference should now be tracked.
+ assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size());
+
+ // Test lowest to highest priority requests.
+ // Bring up metered cellular. This will satisfy the fallback network.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mService.mNoServiceNetwork.network());
+
+ // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE.
+ startOemManagedNetwork(false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mWiFiNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mEthernetNetworkAgent.getNetwork());
+
+ // Disconnecting OEM_PRIVATE will keep the fallback on cellular.
+ // OEM_PRIVATE_ONLY not supporting a fallback now uses to the disconnected network.
+ stopOemManagedNetwork();
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ mCellNetworkAgent.getNetwork(),
+ mService.mNoServiceNetwork.network());
+
+ // Disconnecting cellular will put the fallback on null and pref on disconnected.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false);
+ verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize,
+ null,
+ mService.mNoServiceNetwork.network());
+
+ // default NCs will be unregistered in tearDown
+ }
+
+ @Test
+ public void testGetAllNetworkStateSnapshot() throws Exception {
+ verifyNoNetwork();
+
+ // Setup test cellular network with specified LinkProperties and NetworkCapabilities,
+ // verify the content of the snapshot matches.
+ final LinkProperties cellLp = new LinkProperties();
+ final LinkAddress myIpv4Addr = new LinkAddress(InetAddress.getByName("192.0.2.129"), 25);
+ final LinkAddress myIpv6Addr = new LinkAddress(InetAddress.getByName("2001:db8::1"), 64);
+ cellLp.setInterfaceName("test01");
+ cellLp.addLinkAddress(myIpv4Addr);
+ cellLp.addLinkAddress(myIpv6Addr);
+ cellLp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
+ cellLp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
+ cellLp.addRoute(new RouteInfo(myIpv4Addr, null));
+ cellLp.addRoute(new RouteInfo(myIpv6Addr, null));
+ final NetworkCapabilities cellNcTemplate = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_MMS).build();
+
+ final TestNetworkCallback cellCb = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+ cellCb);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp, cellNcTemplate);
+ mCellNetworkAgent.connect(true);
+ cellCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshot();
+ assertLength(1, snapshots);
+
+ // Compose the expected cellular snapshot for verification.
+ final NetworkCapabilities cellNc =
+ mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork());
+ final NetworkStateSnapshot cellSnapshot = new NetworkStateSnapshot(
+ mCellNetworkAgent.getNetwork(), cellNc, cellLp,
+ null, ConnectivityManager.TYPE_MOBILE);
+ assertEquals(cellSnapshot, snapshots.get(0));
+
+ // Connect wifi and verify the snapshots.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ waitForIdle();
+ // Compose the expected wifi snapshot for verification.
+ final NetworkCapabilities wifiNc =
+ mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork());
+ final NetworkStateSnapshot wifiSnapshot = new NetworkStateSnapshot(
+ mWiFiNetworkAgent.getNetwork(), wifiNc, new LinkProperties(), null,
+ ConnectivityManager.TYPE_WIFI);
+
+ snapshots = mCm.getAllNetworkStateSnapshot();
+ assertLength(2, snapshots);
+ assertContainsAll(snapshots, cellSnapshot, wifiSnapshot);
+
+ // Set cellular as suspended, verify the snapshots will not contain suspended networks.
+ // TODO: Consider include SUSPENDED networks, which should be considered as
+ // temporary shortage of connectivity of a connected network.
+ mCellNetworkAgent.suspend();
+ waitForIdle();
+ snapshots = mCm.getAllNetworkStateSnapshot();
+ assertLength(1, snapshots);
+ assertEquals(wifiSnapshot, snapshots.get(0));
+
+ // Disconnect wifi, verify the snapshots contain nothing.
+ mWiFiNetworkAgent.disconnect();
+ waitForIdle();
+ snapshots = mCm.getAllNetworkStateSnapshot();
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertLength(0, snapshots);
+
+ mCellNetworkAgent.resume();
+ waitForIdle();
+ snapshots = mCm.getAllNetworkStateSnapshot();
+ assertLength(1, snapshots);
+ assertEquals(cellSnapshot, snapshots.get(0));
+
+ mCellNetworkAgent.disconnect();
+ waitForIdle();
+ verifyNoNetwork();
+ mCm.unregisterNetworkCallback(cellCb);
}
}
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index f5b85ca06f92..5760211d9a27 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -22,6 +22,8 @@ import static android.net.NetworkCapabilities.MAX_TRANSPORT;
import static android.net.NetworkCapabilities.MIN_TRANSPORT;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
+import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -164,7 +166,8 @@ public class DnsManagerTest {
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
- InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ InetAddress.parseNumericAddress("4.4.4.4"), "",
+ VALIDATION_RESULT_SUCCESS));
LinkProperties fixedLp = new LinkProperties(lp);
mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
assertFalse(fixedLp.isPrivateDnsActive());
@@ -204,7 +207,8 @@ public class DnsManagerTest {
// Validate one.
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("6.6.6.6"), "strictmode.com", true));
+ InetAddress.parseNumericAddress("6.6.6.6"), "strictmode.com",
+ VALIDATION_RESULT_SUCCESS));
fixedLp = new LinkProperties(lp);
mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
assertEquals(Arrays.asList(InetAddress.parseNumericAddress("6.6.6.6")),
@@ -212,7 +216,8 @@ public class DnsManagerTest {
// Validate the 2nd one.
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("2001:db8:66:66::1"), "strictmode.com", true));
+ InetAddress.parseNumericAddress("2001:db8:66:66::1"), "strictmode.com",
+ VALIDATION_RESULT_SUCCESS));
fixedLp = new LinkProperties(lp);
mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
assertEquals(Arrays.asList(
@@ -232,7 +237,8 @@ public class DnsManagerTest {
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ InetAddress.parseNumericAddress("3.3.3.3"), "",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -245,7 +251,8 @@ public class DnsManagerTest {
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
- InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ InetAddress.parseNumericAddress("3.3.3.3"), "",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -253,7 +260,8 @@ public class DnsManagerTest {
// Validation event has untracked ipAddress
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ InetAddress.parseNumericAddress("4.4.4.4"), "",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -261,8 +269,8 @@ public class DnsManagerTest {
// Validation event has untracked hostname
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("3.3.3.3"), "hostname",
- true));
+ InetAddress.parseNumericAddress("3.3.3.3"), "hostname",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -270,7 +278,8 @@ public class DnsManagerTest {
// Validation event failed
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("3.3.3.3"), "", false));
+ InetAddress.parseNumericAddress("3.3.3.3"), "",
+ VALIDATION_RESULT_FAILURE));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -279,7 +288,7 @@ public class DnsManagerTest {
mDnsManager.removeNetwork(new Network(TEST_NETID));
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ InetAddress.parseNumericAddress("3.3.3.3"), "", VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -293,7 +302,8 @@ public class DnsManagerTest {
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
- InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ InetAddress.parseNumericAddress("3.3.3.3"), "",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
assertFalse(lp.isPrivateDnsActive());
assertNull(lp.getPrivateDnsServerName());
@@ -398,7 +408,8 @@ public class DnsManagerTest {
mDnsManager.updatePrivateDns(network, mDnsManager.getPrivateDnsConfig());
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
mDnsManager.updatePrivateDnsValidation(
- new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, dnsAddr, "", true));
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, dnsAddr, "",
+ VALIDATION_RESULT_SUCCESS));
mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
assertTrue(privateDnsCfg.useTls);
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 52cb836e19c8..a913673c2a1e 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -41,7 +41,6 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkProvider;
import android.os.Binder;
-import android.os.INetworkManagementService;
import android.text.format.DateUtils;
import androidx.test.filters.SmallTest;
@@ -74,7 +73,6 @@ public class LingerMonitorTest {
@Mock ConnectivityService mConnService;
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
- @Mock INetworkManagementService mNMS;
@Mock Context mCtx;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
@@ -358,8 +356,8 @@ public class LingerMonitorTest {
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
- mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE,
- Binder.getCallingUid(), mQosCallbackTracker);
+ mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+ mQosCallbackTracker);
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 4f65b67fa3da..5f56e25356c2 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -36,7 +36,6 @@ import android.net.LinkProperties;
import android.net.NetworkAgentConfig;
import android.net.NetworkInfo;
import android.os.Handler;
-import android.os.INetworkManagementService;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
@@ -67,7 +66,6 @@ public class Nat464XlatTest {
@Mock ConnectivityService mConnectivity;
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
- @Mock INetworkManagementService mNms;
@Mock NetworkAgentInfo mNai;
TestLooper mLooper;
@@ -75,7 +73,7 @@ public class Nat464XlatTest {
NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
Nat464Xlat makeNat464Xlat() {
- return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
+ return new Nat464Xlat(mNai, mNetd, mDnsResolver) {
@Override protected int getNetId() {
return NETID;
}
@@ -206,7 +204,6 @@ public class Nat464XlatTest {
// Start clat.
nat.start();
- verify(mNms).registerObserver(eq(nat));
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// Stacked interface up notification arrives.
@@ -225,7 +222,6 @@ public class Nat464XlatTest {
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
- verify(mNms).unregisterObserver(eq(nat));
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
@@ -235,7 +231,7 @@ public class Nat464XlatTest {
nat.interfaceRemoved(STACKED_IFACE);
mLooper.dispatchNext();
- verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ verifyNoMoreInteractions(mNetd, mConnectivity);
}
@Test
@@ -346,7 +342,6 @@ public class Nat464XlatTest {
nat.start();
- verify(mNms).registerObserver(eq(nat));
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// Stacked interface up notification arrives.
@@ -365,7 +360,6 @@ public class Nat464XlatTest {
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
- verify(mNms).unregisterObserver(eq(nat));
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -374,7 +368,7 @@ public class Nat464XlatTest {
// ConnectivityService stops clat: no-op.
nat.stop();
- verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ verifyNoMoreInteractions(mNetd, mConnectivity);
}
private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
@@ -386,7 +380,6 @@ public class Nat464XlatTest {
nat.start();
- verify(mNms).registerObserver(eq(nat));
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
@@ -394,7 +387,6 @@ public class Nat464XlatTest {
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
- verify(mNms).unregisterObserver(eq(nat));
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
@@ -408,7 +400,7 @@ public class Nat464XlatTest {
assertIdle(nat);
- verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ verifyNoMoreInteractions(mNetd, mConnectivity);
}
@Test
@@ -430,7 +422,6 @@ public class Nat464XlatTest {
nat.start();
- verify(mNms).registerObserver(eq(nat));
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
@@ -438,11 +429,10 @@ public class Nat464XlatTest {
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
- verify(mNms).unregisterObserver(eq(nat));
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
- verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ verifyNoMoreInteractions(mNetd, mConnectivity);
}
@Test
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/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 54d6fb9f2c12..9334e2c4ad77 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -19,9 +19,7 @@ package com.android.server.net;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkIdentity.OEM_PAID;
import static android.net.NetworkIdentity.OEM_PRIVATE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
@@ -86,7 +84,7 @@ import android.net.INetworkStatsSession;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkState;
+import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
@@ -286,7 +284,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -329,7 +327,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -403,7 +401,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -444,7 +442,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testUidStatsAcrossNetworks() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -475,7 +473,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// disappearing, to verify we don't count backwards.
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- states = new NetworkState[] {buildMobile3gState(IMSI_2)};
+ states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
@@ -519,7 +517,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testUidRemovedIsMoved() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -583,7 +581,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
final NetworkTemplate template5g =
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
- final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+ final NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
// 3G network comes online.
expectNetworkStatsSummary(buildEmptyStats());
@@ -673,7 +672,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO);
// OEM_PAID network comes online.
- NetworkState[] states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
+ buildOemManagedMobileState(IMSI_1, false,
new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -688,7 +688,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
forcePollAndWaitForIdle();
// OEM_PRIVATE network comes online.
- states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false,
new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -703,7 +703,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
forcePollAndWaitForIdle();
// OEM_PAID + OEM_PRIVATE network comes online.
- states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false,
new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
NetworkCapabilities.NET_CAPABILITY_OEM_PAID})};
expectNetworkStatsSummary(buildEmptyStats());
@@ -719,7 +719,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
forcePollAndWaitForIdle();
// OEM_NONE network comes online.
- states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})};
+ states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
@@ -771,7 +771,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testSummaryForAllUid() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -830,7 +830,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testDetailedUidStats() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -871,9 +871,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
final String stackedIface = "stacked-test0";
final LinkProperties stackedProp = new LinkProperties();
stackedProp.setInterfaceName(stackedIface);
- final NetworkState wifiState = buildWifiState();
+ final NetworkStateSnapshot wifiState = buildWifiState();
wifiState.linkProperties.addStackedLink(stackedProp);
- NetworkState[] states = new NetworkState[] {wifiState};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {wifiState};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -929,7 +929,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testForegroundBackground() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -986,8 +986,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testMetered() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states =
- new NetworkState[] {buildWifiState(true /* isMetered */, TEST_IFACE)};
+ NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[] {buildWifiState(true /* isMetered */, TEST_IFACE)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1026,8 +1026,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testRoaming() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- NetworkState[] states =
- new NetworkState[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
+ NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1065,7 +1065,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testTethering() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+ final NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1122,7 +1123,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildWifiState()};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1220,8 +1221,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testStatsProviderUpdateStats() throws Exception {
// Pretend that network comes online.
expectDefaultSettings();
- final NetworkState[] states =
- new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
+ final NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1282,8 +1283,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testStatsProviderSetAlert() throws Exception {
// Pretend that network comes online.
expectDefaultSettings();
- NetworkState[] states =
- new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
+ NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
new UnderlyingNetworkInfo[0]);
@@ -1326,7 +1327,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN);
final NetworkTemplate templateAll =
buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL);
- final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+ final NetworkStateSnapshot[] states =
+ new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1401,7 +1403,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
public void testOperationCount_nonDefault_traffic() throws Exception {
// Pretend mobile network comes online, but wifi is the default network.
expectDefaultSettings();
- NetworkState[] states = new NetworkState[]{
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)};
expectNetworkStatsUidDetail(buildEmptyStats());
mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
@@ -1489,7 +1491,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectNetworkStatsSummary(buildEmptyStats());
}
- private String getActiveIface(NetworkState... states) throws Exception {
+ private String getActiveIface(NetworkStateSnapshot... states) throws Exception {
if (states == null || states.length == 0 || states[0].linkProperties == null) {
return null;
}
@@ -1565,11 +1567,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
assertEquals("unexpected operations", operations, entry.operations);
}
- private static NetworkState buildWifiState() {
+ private static NetworkStateSnapshot buildWifiState() {
return buildWifiState(false, TEST_IFACE);
}
- private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
+ private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1577,35 +1579,30 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
capabilities.setSSID(TEST_SSID);
- return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null);
+ return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI);
}
- private static NetworkState buildMobile3gState(String subscriberId) {
+ private static NetworkStateSnapshot buildMobile3gState(String subscriberId) {
return buildMobile3gState(subscriberId, false /* isRoaming */);
}
- private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
+ private static NetworkStateSnapshot buildMobile3gState(String subscriberId, boolean isRoaming) {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId);
+ return new NetworkStateSnapshot(
+ MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE);
}
private NetworkStats buildEmptyStats() {
return new NetworkStats(getElapsedRealtime(), 0);
}
- private static NetworkState buildVpnState() {
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TUN_IFACE);
- return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null);
- }
-
- private static NetworkState buildOemManagedMobileState(String subscriberId, boolean isRoaming,
- int[] oemNetCapabilities) {
+ private static NetworkStateSnapshot buildOemManagedMobileState(
+ String subscriberId, boolean isRoaming, int[] oemNetCapabilities) {
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1615,7 +1612,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
capabilities.setCapability(nc, true);
}
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId);
+ return new NetworkStateSnapshot(MOBILE_NETWORK, capabilities, prop, subscriberId,
+ TYPE_MOBILE);
}
private long getElapsedRealtime() {
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/IkeTrafficSelectorUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java
new file mode 100644
index 000000000000..28cf38a2a583
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.InetAddresses;
+import android.net.ipsec.ike.IkeTrafficSelector;
+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.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IkeTrafficSelectorUtilsTest {
+ private static final int START_PORT = 16;
+ private static final int END_PORT = 65520;
+
+ private static final InetAddress IPV4_START_ADDRESS =
+ InetAddresses.parseNumericAddress("192.0.2.100");
+ private static final InetAddress IPV4_END_ADDRESS =
+ InetAddresses.parseNumericAddress("192.0.2.101");
+
+ private static final InetAddress IPV6_START_ADDRESS =
+ InetAddresses.parseNumericAddress("2001:db8:2::100");
+ private static final InetAddress IPV6_END_ADDRESS =
+ InetAddresses.parseNumericAddress("2001:db8:2::101");
+
+ private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeTrafficSelector ts) {
+ final PersistableBundle bundle = IkeTrafficSelectorUtils.toPersistableBundle(ts);
+ final IkeTrafficSelector resultTs = IkeTrafficSelectorUtils.fromPersistableBundle(bundle);
+ assertEquals(ts, resultTs);
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessIpv4Ts() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeTrafficSelector(START_PORT, END_PORT, IPV4_START_ADDRESS, IPV4_END_ADDRESS));
+ }
+
+ @Test
+ public void testPersistableBundleEncodeDecodeIsLosslessIpv6Ts() throws Exception {
+ verifyPersistableBundleEncodeDecodeIsLossless(
+ new IkeTrafficSelector(START_PORT, END_PORT, IPV6_START_ADDRESS, IPV6_END_ADDRESS));
+ }
+}
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/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 69c21b967917..69b2fb135a8d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -36,6 +36,7 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -143,11 +144,18 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
+ verify(mIpSecSvc, times(2))
+ .setNetworkForTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
+ eq(TEST_UNDERLYING_NETWORK_RECORD_1.network),
+ any());
+
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
verify(mIpSecSvc)
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
+
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
@@ -290,4 +298,22 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback(
new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR);
}
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index 17ae19e086cf..d07d2cf4f1bb 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -19,6 +19,8 @@ package com.android.server.vcn;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -111,4 +113,22 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
public void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
}
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 9ea641f52e48..5f27fabb62b0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -21,9 +21,12 @@ import static android.net.IpSecManager.IpSecTunnelInterface;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.net.IpSecManager;
@@ -54,7 +57,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
}
@Test
- public void testEnterWhileNotRunningTriggersQuit() throws Exception {
+ public void testEnterWhileQuittingTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
new VcnGatewayConnection(
mVcnContext,
@@ -64,7 +67,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
mGatewayStatusCallback,
mDeps);
- vgc.setIsRunning(false);
+ vgc.setIsQuitting(true);
vgc.transitionTo(vgc.mDisconnectedState);
mTestLooper.dispatchAll();
@@ -101,5 +104,18 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
assertNull(mGatewayConnection.getCurrentState());
verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */);
+ assertTrue(mGatewayConnection.isQuitting());
+ verify(mGatewayStatusCallback).onQuit();
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ verify(mGatewayStatusCallback, never()).onQuit();
+ // No safe mode timer changes expected.
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 7385204993c0..661e03af4f84 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -18,6 +18,8 @@ package com.android.server.vcn;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -79,10 +81,20 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec
// Should do nothing; already tearing down.
assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ assertTrue(mGatewayConnection.isQuitting());
}
@Test
public void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
}
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 5b0850b03f1a..85a0277f8b48 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -17,6 +17,9 @@
package com.android.server.vcn;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -96,4 +99,22 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
public void testSafeModeTimeoutNotifiesCallback() {
verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
}
+
+ @Test
+ public void testTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isQuitting());
+ }
+
+ @Test
+ public void testNonTeardownDisconnectRequest() throws Exception {
+ mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ assertFalse(mGatewayConnection.isQuitting());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 9d3368271243..3dd710afed7b 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,6 +16,10 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
@@ -33,6 +37,7 @@ import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -51,6 +56,11 @@ public class VcnTest {
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0;
private static final int PROVIDER_ID = 5;
+ private static final int[][] TEST_CAPS =
+ new int[][] {
+ new int[] {NET_CAPABILITY_INTERNET, NET_CAPABILITY_MMS},
+ new int[] {NET_CAPABILITY_DUN}
+ };
private Context mContext;
private VcnContext mVcnContext;
@@ -91,13 +101,12 @@ public class VcnTest {
mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
- for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ for (final int[] caps : TEST_CAPS) {
configBuilder.addGatewayConnectionConfig(
- VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(caps));
}
- configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
- mConfig = configBuilder.build();
+ mConfig = configBuilder.build();
mVcn =
new Vcn(
mVcnContext,
@@ -130,8 +139,7 @@ public class VcnTest {
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
- startVcnGatewayWithCapabilities(
- requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+ startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -153,10 +161,19 @@ public class VcnTest {
for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
startVcnGatewayWithCapabilities(requestListener, capability);
}
+ }
+
+ private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
+ for (final int[] caps : TEST_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, caps);
+ }
+ }
- // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
- // Expect one VcnGatewayConnection per capability.
- final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+ public Set<VcnGatewayConnection> startGatewaysAndGetGatewayConnections(
+ NetworkRequestListener requestListener) {
+ triggerVcnRequestListeners(requestListener);
+
+ final int numExpectedGateways = TEST_CAPS.length;
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertEquals(numExpectedGateways, gatewayConnections.size());
@@ -168,7 +185,16 @@ public class VcnTest {
any(),
mGatewayStatusCallbackCaptor.capture());
- // Doesn't matter which callback this gets - any Gateway entering safe mode should shut down
+ return gatewayConnections;
+ }
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ final Set<VcnGatewayConnection> gatewayConnections =
+ startGatewaysAndGetGatewayConnections(requestListener);
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
// all Gateways
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
statusCallback.onEnteredSafeMode();
@@ -181,4 +207,31 @@ public class VcnTest {
verify(mVcnNetworkProvider).unregisterListener(requestListener);
verify(mVcnCallback).onEnteredSafeMode();
}
+
+ @Test
+ public void testGatewayQuit() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ final Set<VcnGatewayConnection> gatewayConnections =
+ new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
+
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onQuit();
+ mTestLooper.dispatchAll();
+
+ // Verify that the VCN requests the networkRequests be resent
+ assertEquals(1, mVcn.getVcnGatewayConnections().size());
+ verify(mVcnNetworkProvider).resendAllRequests(requestListener);
+
+ // Verify that the VcnGatewayConnection is restarted
+ triggerVcnRequestListeners(requestListener);
+ mTestLooper.dispatchAll();
+ assertEquals(2, mVcn.getVcnGatewayConnections().size());
+ verify(mDeps, times(gatewayConnections.size() + 1))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+ }
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b760958e0edc..13e090d9d843 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2263,6 +2263,16 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
return 1;
}
+ if (shared_lib_ && options_.private_symbols) {
+ // If a shared library styleable in a public R.java uses a private attribute, attempting to
+ // reference the private attribute within the styleable array will cause a link error because
+ // the private attribute will not be emitted in the public R.java.
+ context.GetDiagnostics()->Error(DiagMessage()
+ << "--shared-lib cannot currently be used in combination with"
+ << " --private-symbols");
+ return 1;
+ }
+
if (options_.merge_only && !static_lib_) {
context.GetDiagnostics()->Error(
DiagMessage() << "the --merge-only flag can be only used when building a static library");
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 062dd8eac975..73072a963d09 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-#include "AppInfo.h"
#include "Link.h"
+#include <android-base/file.h>
+
+#include "AppInfo.h"
#include "LoadedApk.h"
#include "test/Test.h"
using testing::Eq;
+using testing::HasSubstr;
using testing::Ne;
namespace aapt {
@@ -317,4 +320,76 @@ TEST_F(LinkTest, AppInfoWithUsesSplit) {
ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
}
+TEST_F(LinkTest, SharedLibraryAttributeRJava) {
+ StdErrDiagnostics diag;
+ const std::string lib_values =
+ R"(<resources>
+ <attr name="foo"/>
+ <public type="attr" name="foo" id="0x00010001"/>
+ <declare-styleable name="LibraryStyleable">
+ <attr name="foo" />
+ </declare-styleable>
+ </resources>)";
+
+ const std::string client_values =
+ R"(<resources>
+ <attr name="bar" />
+ <declare-styleable name="ClientStyleable">
+ <attr name="com.example.lib:foo" />
+ <attr name="bar" />
+ </declare-styleable>
+ </resources>)";
+
+ // Build a library with a public attribute
+ const std::string lib_res = GetTestPath("library-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), lib_values, lib_res, &diag));
+
+ const std::string lib_apk = GetTestPath("library.apk");
+ const std::string lib_java = GetTestPath("library_java");
+ // clang-format off
+ auto lib_manifest = ManifestBuilder(this)
+ .SetPackageName("com.example.lib")
+ .Build();
+
+ auto lib_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(lib_manifest)
+ .AddFlag("--shared-lib")
+ .AddParameter("--java", lib_java)
+ .AddCompiledResDir(lib_res, &diag)
+ .Build(lib_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(lib_link_args, &diag));
+
+ const std::string lib_r_java = lib_java + "/com/example/lib/R.java";
+ std::string lib_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(lib_r_java, &lib_r_contents));
+ EXPECT_THAT(lib_r_contents, HasSubstr(" public static int foo=0x00010001;"));
+ EXPECT_THAT(lib_r_contents, HasSubstr(" com.example.lib.R.attr.foo"));
+
+ // Build a client that uses the library attribute in a declare-styleable
+ const std::string client_res = GetTestPath("client-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), client_values, client_res, &diag));
+
+ const std::string client_apk = GetTestPath("client.apk");
+ const std::string client_java = GetTestPath("client_java");
+ // clang-format off
+ auto client_manifest = ManifestBuilder(this)
+ .SetPackageName("com.example.client")
+ .Build();
+
+ auto client_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(client_manifest)
+ .AddParameter("--java", client_java)
+ .AddParameter("-I", lib_apk)
+ .AddCompiledResDir(client_res, &diag)
+ .Build(client_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(client_link_args, &diag));
+
+ const std::string client_r_java = client_java + "/com/example/client/R.java";
+ std::string client_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
+ EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index eb0ade62d542..4b90b4f534cc 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -570,7 +570,6 @@ class PackageFlattener {
ResourceEntry* entry = sorted_entries->at(entryIndex);
// Populate the config masks for this entry.
-
if (entry->visibility.level == Visibility::Level::kPublic) {
config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 1e4b6816075a..995495ac56a8 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -70,8 +70,8 @@ class PrimitiveMember : public ClassMember {
return name_;
}
- void Print(bool final, text::Printer* printer, bool strip_api_annotations = false)
- const override {
+ void Print(bool final, text::Printer* printer,
+ bool strip_api_annotations = false) const override {
using std::to_string;
ClassMember::Print(final, printer, strip_api_annotations);
@@ -127,13 +127,13 @@ using IntMember = PrimitiveMember<uint32_t>;
using ResourceMember = PrimitiveMember<ResourceId>;
using StringMember = PrimitiveMember<std::string>;
-template <typename T>
+template <typename T, typename StringConverter>
class PrimitiveArrayMember : public ClassMember {
public:
explicit PrimitiveArrayMember(const android::StringPiece& name) : name_(name.to_string()) {}
void AddElement(const T& val) {
- elements_.push_back(val);
+ elements_.emplace_back(val);
}
bool empty() const override {
@@ -158,7 +158,7 @@ class PrimitiveArrayMember : public ClassMember {
printer->Println();
}
- printer->Print(to_string(*current));
+ printer->Print(StringConverter::ToString(*current));
if (std::distance(current, end) > 1) {
printer->Print(", ");
}
@@ -175,7 +175,24 @@ class PrimitiveArrayMember : public ClassMember {
std::vector<T> elements_;
};
-using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
+struct FieldReference {
+ explicit FieldReference(std::string reference) : ref(std::move(reference)) {
+ }
+ std::string ref;
+};
+
+struct ResourceArrayMemberStringConverter {
+ static std::string ToString(const std::variant<ResourceId, FieldReference>& ref) {
+ if (auto id = std::get_if<ResourceId>(&ref)) {
+ return to_string(*id);
+ } else {
+ return std::get<FieldReference>(ref).ref;
+ }
+ }
+};
+
+using ResourceArrayMember = PrimitiveArrayMember<std::variant<ResourceId, FieldReference>,
+ ResourceArrayMemberStringConverter>;
// Represents a method in a class.
class MethodDefinition : public ClassMember {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index f0f839d968d5..59dd481607e9 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -224,7 +224,16 @@ static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
return cmp_ids_dynamic_after_framework(lhs_id, rhs_id);
}
-void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
+static FieldReference GetRFieldReference(const ResourceName& name,
+ StringPiece fallback_package_name) {
+ const std::string package_name =
+ name.package.empty() ? fallback_package_name.to_string() : name.package;
+ const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
+ return FieldReference(
+ StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+}
+
+bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
const Styleable& styleable,
const StringPiece& package_name_to_generate,
ClassDefinition* out_class_def,
@@ -340,14 +349,29 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
// Add the ResourceIds to the array member.
for (size_t i = 0; i < attr_count; i++) {
- const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
- array_def->AddElement(id);
+ const StyleableAttr& attr = sorted_attributes[i];
+ std::string r_txt_contents;
+ if (attr.symbol && attr.symbol.value().is_dynamic) {
+ if (!attr.attr_ref->name) {
+ error_ = "unable to determine R.java field name of dynamic resource";
+ return false;
+ }
+
+ const FieldReference field_name =
+ GetRFieldReference(attr.attr_ref->name.value(), package_name_to_generate);
+ array_def->AddElement(field_name);
+ r_txt_contents = field_name.ref;
+ } else {
+ const ResourceId attr_id = attr.attr_ref->id.value_or_default(ResourceId(0));
+ array_def->AddElement(attr_id);
+ r_txt_contents = to_string(attr_id);
+ }
if (r_txt_printer != nullptr) {
if (i != 0) {
r_txt_printer->Print(",");
}
- r_txt_printer->Print(" ").Print(id.to_string());
+ r_txt_printer->Print(" ").Print(r_txt_contents);
}
}
@@ -419,19 +443,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
}
}
- // If there is a rewrite method to generate, add the statements that rewrite package IDs
- // for this styleable.
- if (out_rewrite_method != nullptr) {
- out_rewrite_method->AppendStatement(
- StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
- out_rewrite_method->AppendStatement(
- StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
- out_rewrite_method->AppendStatement(
- StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | packageIdBits;",
- array_field_name.data(), array_field_name.data()));
- out_rewrite_method->AppendStatement(" }");
- out_rewrite_method->AppendStatement("}");
- }
+ return true;
}
void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
@@ -448,8 +460,7 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
const std::string field_name = TransformToFieldName(name.entry);
if (out_class_def != nullptr) {
- std::unique_ptr<ResourceMember> resource_member =
- util::make_unique<ResourceMember>(field_name, real_id);
+ auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
// Build the comments and annotations for this entry.
AnnotationProcessor* processor = resource_member->GetCommentBuilder();
@@ -551,12 +562,11 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
if (resource_name.type == ResourceType::kStyleable) {
CHECK(!entry->values.empty());
-
- const Styleable* styleable =
- static_cast<const Styleable*>(entry->values.front()->value.get());
-
- ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
- out_rewrite_method_def, r_txt_printer);
+ const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
+ if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
+ out_type_class_def, out_rewrite_method_def, r_txt_printer)) {
+ return false;
+ }
} else {
ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
r_txt_printer);
@@ -626,8 +636,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
if (type->type == ResourceType::kAttr) {
// Also include private attributes in this same class.
- const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
- if (priv_type) {
+ if (const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate)) {
if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
rewrite_method.get(), r_txt_printer.get())) {
return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 853120b3cb98..d9d1b39805f9 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -105,7 +105,7 @@ class JavaClassGenerator {
// Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
// its package ID if `out_rewrite_method` is not nullptr.
// `package_name_to_generate` is the package
- void ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
+ bool ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
const Styleable& styleable,
const android::StringPiece& package_name_to_generate,
ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 04e20101a0dd..ec5b4151b1a6 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -581,7 +581,7 @@ TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
out.Flush();
EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
- EXPECT_THAT(output, HasSubstr("0x01010000, 0x00010000"));
+ EXPECT_THAT(output, HasSubstr("0x01010000, lib.R.attr.dynamic_attr"));
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index daedc2a14767..98ee63d2e5c6 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -370,11 +370,11 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = res_id;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
}
if (s) {
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
return s;
}
return {};
@@ -417,11 +417,11 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
}
if (s) {
s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
return s;
}
return {};
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 5386802dbc8e..f94f0fe1144a 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -18,18 +18,17 @@
#include <dirent.h>
-#include "android-base/errors.h"
-#include "android-base/file.h"
-#include "android-base/stringprintf.h"
-#include "android-base/utf8.h"
-#include "androidfw/StringPiece.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <android-base/errors.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/utf8.h>
+#include <androidfw/StringPiece.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include "cmd/Compile.h"
#include "cmd/Link.h"
#include "io/FileStream.h"
-#include "io/Util.h"
#include "util/Files.h"
using testing::Eq;
@@ -170,4 +169,74 @@ void CommandTestFixture::AssertLoadXml(LoadedApk* apk, const io::IData* data,
}
}
+ManifestBuilder::ManifestBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
+}
+
+ManifestBuilder& ManifestBuilder::SetPackageName(const std::string& package_name) {
+ package_name_ = package_name;
+ return *this;
+}
+
+ManifestBuilder& ManifestBuilder::AddContents(const std::string& contents) {
+ contents_ += contents + "\n";
+ return *this;
+}
+
+std::string ManifestBuilder::Build(const std::string& file_path) {
+ const char* manifest_template = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="%s">
+ %s
+ </manifest>)";
+
+ fixture_->WriteFile(file_path, android::base::StringPrintf(
+ manifest_template, package_name_.c_str(), contents_.c_str()));
+ return file_path;
+}
+
+std::string ManifestBuilder::Build() {
+ return Build(fixture_->GetTestPath("AndroidManifest.xml"));
+}
+
+LinkCommandBuilder::LinkCommandBuilder(CommandTestFixture* fixture) : fixture_(fixture) {
+}
+
+LinkCommandBuilder& LinkCommandBuilder::SetManifestFile(const std::string& file) {
+ manifest_supplied_ = true;
+ args_.emplace_back("--manifest");
+ args_.emplace_back(file);
+ return *this;
+}
+
+LinkCommandBuilder& LinkCommandBuilder::AddFlag(const std::string& flag) {
+ args_.emplace_back(flag);
+ return *this;
+}
+
+LinkCommandBuilder& LinkCommandBuilder::AddCompiledResDir(const std::string& dir,
+ IDiagnostics* diag) {
+ if (auto files = file::FindFiles(dir, diag)) {
+ for (std::string& compile_file : files.value()) {
+ args_.emplace_back(file::BuildPath({dir, compile_file}));
+ }
+ }
+ return *this;
+}
+
+LinkCommandBuilder& LinkCommandBuilder::AddParameter(const std::string& param,
+ const std::string& value) {
+ args_.emplace_back(param);
+ args_.emplace_back(value);
+ return *this;
+}
+
+std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) {
+ if (!manifest_supplied_) {
+ SetManifestFile(ManifestBuilder(fixture_).Build());
+ }
+ args_.emplace_back("-o");
+ args_.emplace_back(out_apk);
+ return args_;
+}
+
} // namespace aapt \ No newline at end of file
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 457d65e30b65..f8c4889aee3b 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -32,7 +32,7 @@ namespace aapt {
class TestDirectoryFixture : public ::testing::Test {
public:
TestDirectoryFixture() = default;
- virtual ~TestDirectoryFixture() = default;
+ ~TestDirectoryFixture() override = default;
// Creates the test directory or clears its contents if it contains previously created files.
void SetUp() override;
@@ -41,14 +41,14 @@ class TestDirectoryFixture : public ::testing::Test {
void TearDown() override;
// Retrieve the test directory of the fixture.
- const android::StringPiece GetTestDirectory() {
+ android::StringPiece GetTestDirectory() {
return temp_dir_;
}
// Retrieves the absolute path of the specified relative path in the test directory. Directories
// should be separated using forward slashes ('/'), and these slashes will be translated to
// backslashes when running Windows tests.
- const std::string GetTestPath(const android::StringPiece& path) {
+ std::string GetTestPath(const android::StringPiece& path) {
std::string base = temp_dir_;
for (android::StringPiece part : util::Split(path, '/')) {
file::AppendPath(&base, part);
@@ -68,7 +68,7 @@ class TestDirectoryFixture : public ::testing::Test {
class CommandTestFixture : public TestDirectoryFixture {
public:
CommandTestFixture() = default;
- virtual ~CommandTestFixture() = default;
+ ~CommandTestFixture() override = default;
// Wries the contents of the file to the specified path. The file is compiled and the flattened
// file is written to the out directory.
@@ -99,6 +99,33 @@ class CommandTestFixture : public TestDirectoryFixture {
DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
};
+struct ManifestBuilder {
+ explicit ManifestBuilder(CommandTestFixture* fixture);
+ ManifestBuilder& AddContents(const std::string& contents);
+ ManifestBuilder& SetPackageName(const std::string& package_name);
+ std::string Build(const std::string& file_path);
+ std::string Build();
+
+ private:
+ CommandTestFixture* fixture_;
+ std::string package_name_ = CommandTestFixture::kDefaultPackageName;
+ std::string contents_;
+};
+
+struct LinkCommandBuilder {
+ explicit LinkCommandBuilder(CommandTestFixture* fixture);
+ LinkCommandBuilder& AddCompiledResDir(const std::string& dir, IDiagnostics* diag);
+ LinkCommandBuilder& AddFlag(const std::string& flag);
+ LinkCommandBuilder& AddParameter(const std::string& param, const std::string& value);
+ LinkCommandBuilder& SetManifestFile(const std::string& manifest_path);
+ std::vector<std::string> Build(const std::string& out_apk_path);
+
+ private:
+ CommandTestFixture* fixture_;
+ std::vector<std::string> args_;
+ bool manifest_supplied_ = false;
+};
+
} // namespace aapt
#endif // AAPT_TEST_FIXTURE_H \ No newline at end of file
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;
}
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index ebc0ec1640f5..e775e1adb04e 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -11,12 +11,6 @@ from fontTools import ttLib
EMOJI_VS = 0xFE0F
-#TODO(179952916): Rename CutiveMono and DancingScript
-CANONICAL_NAME_EXCEPTION_LIST = [
- 'CutiveMono.ttf',
- 'DancingScript-Regular.ttf',
-]
-
LANG_TO_SCRIPT = {
'as': 'Beng',
'be': 'Cyrl',
@@ -703,8 +697,6 @@ def getSuffix(font):
def check_canonical_name():
for record in _all_fonts:
file_name, index = record.font
- if file_name in CANONICAL_NAME_EXCEPTION_LIST:
- continue
if index and index != 0:
continue