summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-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/jobscheduler/framework/java/android/os/PowerWhitelistManager.java77
-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.java280
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java13
-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/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java8
-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.txt334
-rw-r--r--core/api/module-lib-current.txt30
-rw-r--r--core/api/system-current.txt357
-rw-r--r--core/api/test-current.txt36
-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/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/bluetooth/BluetoothDevice.java126
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java2
-rw-r--r--core/java/android/companion/AssociationRequest.java4
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/pm/AppSearchPerson.java2
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java229
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java12
-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/ShortcutInfo.java6
-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/devicestate/DeviceStateInfo.aidl19
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateInfo.java162
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java61
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java155
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateRequest.java6
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManager.aidl9
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl23
-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/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
-rwxr-xr-xcore/java/android/os/Build.java8
-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.java8
-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.java36
-rw-r--r--core/java/android/provider/Telephony.java8
-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/SparseArray.java7
-rw-r--r--core/java/android/util/apk/OWNERS1
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java3
-rw-r--r--core/java/android/view/AppTransitionAnimationSpec.java8
-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.java67
-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/AccessibilityNodeInfo.java6
-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.java8
-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/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/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_media_AudioRecord.cpp103
-rw-r--r--core/jni/android_media_AudioTrack.cpp7
-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/providers/settings/global.proto3
-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_manifest.xml2
-rw-r--r--core/res/res/values/colors_device_defaults.xml7
-rw-r--r--core/res/res/values/config.xml34
-rw-r--r--core/res/res/values/dimens.xml30
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/strings.xml26
-rw-r--r--core/res/res/values/symbols.xml27
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java3
-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/content/pm/AppSearchShortcutInfoTest.java5
-rw-r--r--core/tests/coretests/src/android/graphics/FontListParserTest.java109
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java29
-rw-r--r--core/tests/coretests/src/android/security/CredentialManagementAppTest.java35
-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/devicestatetests/Android.bp1
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java127
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java182
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java2
-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.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/pip/PipBoundsAlgorithm.java8
-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/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.java7
-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--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/AudioRecord.java27
-rw-r--r--media/java/android/media/AudioTrack.java28
-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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-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_enroll.xml34
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml (renamed from packages/SystemUI/res/layout/udfps_animation_view.xml)5
-rw-r--r--packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml22
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml11
-rw-r--r--packages/SystemUI/res/values/config.xml7
-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/RemoteAnimationTargetCompat.java7
-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.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/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/widget/PeopleSpaceWidgetEnabler.java68
-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/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/StatusBarIconView.java10
-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.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/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.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/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/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/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.java2
-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.java523
-rw-r--r--services/core/java/com/android/server/ConnectivityServiceInitializer.java16
-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.java15
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java27
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java28
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java9
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java8
-rw-r--r--services/core/java/com/android/server/am/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/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/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/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.java169
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java4
-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.java72
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java30
-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/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.java55
-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.java123
-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/DisplayFoldController.java6
-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.java44
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java4
-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/VibratorManagerService.java25
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java160
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java2
-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/NonAppWindowAnimationAdapter.java65
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java16
-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.java65
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java242
-rw-r--r--services/core/java/com/android/server/wm/Transition.java20
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java50
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java113
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java70
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java159
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java33
-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/job/controllers/QuotaControllerTest.java40
-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/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/compat/CompatConfigTest.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java222
-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.java73
-rw-r--r--services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java63
-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/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/VibratorManagerServiceTest.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java6
-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/RemoteAnimationControllerTest.java164
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java93
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java15
-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.java619
-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
776 files changed, 25278 insertions, 9677 deletions
diff --git a/Android.bp b/Android.bp
index 908280e5d7f3..9374c01066e6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
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/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/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..460763ad4bbc 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,20 +74,48 @@ class JobConcurrencyManager {
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
- // Try to give higher priority types lower values.
+ /**
+ * Set of possible execution types that a job can have. The actual type(s) of a job are based
+ * on the {@link JobStatus#lastEvaluatedPriority}, which is typically evaluated right before
+ * execution (when we're trying to determine which jobs to run next) and won't change after the
+ * job has started executing.
+ *
+ * Try to give higher priority types lower values.
+ *
+ * @see #getJobWorkTypes(JobStatus)
+ */
+
+ /** Job shouldn't run or qualify as any other work type. */
static final int WORK_TYPE_NONE = 0;
+ /** The job is for an app in the TOP state for a currently active user. */
static final int WORK_TYPE_TOP = 1 << 0;
- static final int WORK_TYPE_EJ = 1 << 1;
- static final int WORK_TYPE_BG = 1 << 2;
- static final int WORK_TYPE_BGUSER = 1 << 3;
+ /**
+ * The job is for an app in a {@link ActivityManager#PROCESS_STATE_FOREGROUND_SERVICE} or higher
+ * state (excluding {@link ActivityManager#PROCESS_STATE_TOP} for a currently active user.
+ */
+ static final int WORK_TYPE_FGS = 1 << 1;
+ /** The job is allowed to run as an expedited job for a currently active user. */
+ static final int WORK_TYPE_EJ = 1 << 2;
+ /**
+ * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP},
+ * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a currently active user, so
+ * can run as a background job.
+ */
+ static final int WORK_TYPE_BG = 1 << 3;
+ /**
+ * The job does not satisfy any of the conditions for {@link #WORK_TYPE_TOP},
+ * {@link #WORK_TYPE_FGS}, or {@link #WORK_TYPE_EJ}, but is for a completely background user,
+ * so can run as a background user job.
+ */
+ static final int WORK_TYPE_BGUSER = 1 << 4;
@VisibleForTesting
- static final int NUM_WORK_TYPES = 4;
- private static final int ALL_WORK_TYPES =
- WORK_TYPE_TOP | WORK_TYPE_EJ | WORK_TYPE_BG | WORK_TYPE_BGUSER;
+ static final int NUM_WORK_TYPES = 5;
+ private static final int ALL_WORK_TYPES = (1 << NUM_WORK_TYPES) - 1;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
WORK_TYPE_NONE,
WORK_TYPE_TOP,
+ WORK_TYPE_FGS,
WORK_TYPE_EJ,
WORK_TYPE_BG,
WORK_TYPE_BGUSER
@@ -95,12 +124,15 @@ class JobConcurrencyManager {
public @interface WorkType {
}
- private static String workTypeToString(@WorkType int workType) {
+ @VisibleForTesting
+ static String workTypeToString(@WorkType int workType) {
switch (workType) {
case WORK_TYPE_NONE:
return "NONE";
case WORK_TYPE_TOP:
return "TOP";
+ case WORK_TYPE_FGS:
+ return "FGS";
case WORK_TYPE_EJ:
return "EJ";
case WORK_TYPE_BG:
@@ -131,58 +163,60 @@ class JobConcurrencyManager {
new WorkConfigLimitsPerMemoryTrimLevel(
new WorkTypeConfig("screen_on_normal", 11,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
new WorkTypeConfig("screen_on_low", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
- Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
- new WorkTypeConfig("screen_on_critical", 5,
+ new WorkTypeConfig("screen_on_critical", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
);
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_off_normal", 13,
+ new WorkTypeConfig("screen_off_normal", 15,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
- new WorkTypeConfig("screen_off_moderate", 13,
+ new WorkTypeConfig("screen_off_moderate", 15,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
- Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
- new WorkTypeConfig("screen_off_low", 7,
+ new WorkTypeConfig("screen_off_low", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
- Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
- new WorkTypeConfig("screen_off_critical", 5,
+ new WorkTypeConfig("screen_off_critical", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_FGS, 1),
+ Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
@@ -436,7 +470,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 +586,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];
@@ -577,7 +612,8 @@ class JobConcurrencyManager {
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 +631,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 +651,7 @@ class JobConcurrencyManager {
}
}
+ @GuardedBy("mLock")
void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
@WorkType final int workType) {
mWorkCountTracker.onJobFinished(workType);
@@ -622,7 +660,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 +731,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 +780,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) {
@@ -848,6 +887,7 @@ class JobConcurrencyManager {
return s.toString();
}
+ @GuardedBy("mLock")
void updateConfigLocked() {
DeviceConfig.Properties properties =
DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
@@ -977,10 +1017,11 @@ class JobConcurrencyManager {
int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0;
- // TODO: create dedicated work type for FGS
if (shouldRunAsFgUserJob(js)) {
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
classification |= WORK_TYPE_TOP;
+ } else if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_FOREGROUND_SERVICE) {
+ classification |= WORK_TYPE_FGS;
} else {
classification |= WORK_TYPE_BG;
}
@@ -1001,11 +1042,13 @@ class JobConcurrencyManager {
private static final String KEY_PREFIX_MAX_TOTAL =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+ private static final String KEY_PREFIX_MAX_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "max_fgs_";
private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
private static final String KEY_PREFIX_MAX_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+ private static final String KEY_PREFIX_MIN_FGS = CONFIG_KEY_PREFIX_CONCURRENCY + "min_fgs_";
private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
private static final String KEY_PREFIX_MIN_BGUSER =
@@ -1053,6 +1096,10 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+ final int maxFgs = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_FGS + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_FGS, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_FGS, maxFgs);
final int maxEj = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
@@ -1074,6 +1121,12 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
remaining -= minTop;
+ // Ensure fgs is in the range [0, min(maxFgs, remaining)]
+ final int minFgs = Math.max(0, Math.min(Math.min(maxFgs, remaining),
+ properties.getInt(KEY_PREFIX_MIN_FGS + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_FGS))));
+ mMinReservedSlots.put(WORK_TYPE_FGS, minFgs);
+ remaining -= minFgs;
// Ensure ej is in the range [0, min(maxEj, remaining)]
final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
@@ -1111,6 +1164,10 @@ class JobConcurrencyManager {
.println();
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
.println();
+ pw.print(KEY_PREFIX_MIN_FGS + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_FGS))
+ .println();
+ pw.print(KEY_PREFIX_MAX_FGS + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_FGS))
+ .println();
pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
.println();
pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
@@ -1205,6 +1262,7 @@ class JobConcurrencyManager {
private int mConfigMaxTotal;
private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mRecycledReserved = new SparseIntArray(NUM_WORK_TYPES);
/**
* Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't
@@ -1220,11 +1278,14 @@ class JobConcurrencyManager {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+ mConfigNumReservedSlots.put(WORK_TYPE_FGS,
+ workTypeConfig.getMinReserved(WORK_TYPE_FGS));
mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_FGS, workTypeConfig.getMax(WORK_TYPE_FGS));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
@@ -1260,15 +1321,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 +1336,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 +1434,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 040a1164fc73..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.
@@ -2293,7 +2299,8 @@ public class JobSchedulerService extends com.android.server.SystemService
/** Returns the maximum amount of time this job could run for. */
public long getMaxJobExecutionTimeMs(JobStatus job) {
synchronized (mLock) {
- return mQuotaController.getMaxJobExecutionTimeMsLocked(job);
+ return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 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/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 2faa8360bf44..b70e68b739d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -995,12 +995,18 @@ public final class QuotaController extends StateController {
if (standbyBucket == NEVER_INDEX) {
return 0;
}
+
List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+ final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
if (sessions == null || sessions.size() == 0) {
+ // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
+ // essentially run until they reach the maximum limit.
+ if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+ return mMaxExecutionTimeMs;
+ }
return mAllowedTimePerPeriodMs;
}
- final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
final long allowedTimeRemainingMs = mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs;
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..a0bec24e65f6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6810,14 +6810,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 {
@@ -11764,6 +11768,7 @@ package android.content.pm {
method public boolean isResourceOverlay();
method public boolean isVirtualPreload();
method public CharSequence loadDescription(android.content.pm.PackageManager);
+ field public static final int CATEGORY_ACCESSIBILITY = 8; // 0x8
field public static final int CATEGORY_AUDIO = 1; // 0x1
field public static final int CATEGORY_GAME = 0; // 0x0
field public static final int CATEGORY_IMAGE = 3; // 0x3
@@ -12943,6 +12948,27 @@ package android.content.pm {
}
+package android.content.pm.verify.domain {
+
+ public final class DomainVerificationManager {
+ method @Nullable public android.content.pm.verify.domain.DomainVerificationUserState getDomainVerificationUserState(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ }
+
+ public final class DomainVerificationUserState implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
+ method @NonNull public String getPackageName();
+ method @NonNull public android.os.UserHandle getUser();
+ method @NonNull public boolean isLinkHandlingAllowed();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserState> CREATOR;
+ field public static final int DOMAIN_STATE_NONE = 0; // 0x0
+ field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
+ field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -17531,6 +17557,9 @@ package android.hardware.biometrics {
public class BiometricManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getButtonLabel(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getPromptMessage(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public CharSequence getSettingName(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
@@ -18565,6 +18594,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 +20325,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 +20383,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 +25925,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 +26067,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 +26748,22 @@ package android.net.vcn {
public class VcnManager {
method @RequiresPermission("carrier privileges") public void clearVcnConfig(@NonNull android.os.ParcelUuid) throws java.io.IOException;
+ method public void registerVcnStatusCallback(@NonNull android.os.ParcelUuid, @NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnStatusCallback);
method @RequiresPermission("carrier privileges") public void setVcnConfig(@NonNull android.os.ParcelUuid, @NonNull android.net.vcn.VcnConfig) throws java.io.IOException;
+ method public void unregisterVcnStatusCallback(@NonNull android.net.vcn.VcnManager.VcnStatusCallback);
+ field public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; // 0x1
+ field public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; // 0x0
+ field public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_ACTIVE = 2; // 0x2
+ field public static final int VCN_STATUS_CODE_INACTIVE = 1; // 0x1
+ field public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; // 0x0
+ field public static final int VCN_STATUS_CODE_SAFE_MODE = 3; // 0x3
+ }
+
+ public abstract static class VcnManager.VcnStatusCallback {
+ ctor public VcnManager.VcnStatusCallback();
+ method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
+ method public abstract void onVcnStatusChanged(int);
}
}
@@ -30228,6 +30313,7 @@ package android.os {
field public static final String HARDWARE;
field public static final String HOST;
field public static final String ID;
+ field public static final boolean IS_DEBUGGABLE;
field public static final String MANUFACTURER;
field public static final String MODEL;
field @NonNull public static final String ODM_SKU;
@@ -30386,19 +30472,10 @@ package android.os {
public abstract class CombinedVibrationEffect implements android.os.Parcelable {
method @NonNull public static android.os.CombinedVibrationEffect createSynced(@NonNull android.os.VibrationEffect);
method public int describeContents();
- method @NonNull public static android.os.CombinedVibrationEffect.SequentialCombination startSequential();
method @NonNull public static android.os.CombinedVibrationEffect.SyncedCombination startSynced();
field @NonNull public static final android.os.Parcelable.Creator<android.os.CombinedVibrationEffect> CREATOR;
}
- public static final class CombinedVibrationEffect.SequentialCombination {
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(int, @NonNull android.os.VibrationEffect, int);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect);
- method @NonNull public android.os.CombinedVibrationEffect.SequentialCombination addNext(@NonNull android.os.CombinedVibrationEffect, int);
- method @NonNull public android.os.CombinedVibrationEffect combine();
- }
-
public static final class CombinedVibrationEffect.SyncedCombination {
method @NonNull public android.os.CombinedVibrationEffect.SyncedCombination addVibrator(int, @NonNull android.os.VibrationEffect);
method @NonNull public android.os.CombinedVibrationEffect combine();
@@ -31823,6 +31900,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 +39026,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 +41394,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 +41512,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 +41548,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 +41557,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 +42031,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 +42231,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 +42255,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 +42284,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
@@ -45848,7 +45932,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 +46328,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 +49805,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 +49835,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 +49953,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 +50117,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 +51019,7 @@ package android.view.displayhash {
method public void onDisplayHashError(int);
method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash);
field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe
+ field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 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..dee910dbd7b8 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";
@@ -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
}
}
@@ -5087,9 +5092,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 +7161,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 +7244,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 +7275,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 +7504,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 +8215,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 +8835,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 +10352,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 +10394,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 +10422,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 +11073,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 +11092,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 +11429,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 +13054,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 +13081,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 +13137,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 +13300,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 +13615,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 +14021,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 +14084,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 316be0bc5625..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 {
@@ -1021,17 +1036,19 @@ package android.hardware.camera2 {
package android.hardware.devicestate {
public final class DeviceStateManager {
- method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
method @NonNull public int[] getSupportedStates();
- method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+ method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+ method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
}
- public static interface DeviceStateManager.DeviceStateListener {
- method public void onDeviceStateChanged(int);
+ public static interface DeviceStateManager.DeviceStateCallback {
+ method public default void onBaseStateChanged(int);
+ method public void onStateChanged(int);
+ method public default void onSupportedStatesChanged(@NonNull int[]);
}
public final class DeviceStateRequest {
@@ -1472,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 {
@@ -1489,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/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/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..17bdd42a0f45 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -60,6 +60,10 @@ public final class AssociationRequest implements Parcelable {
/**
* Device profile: watch.
*
+ * If specified, the current request may have a modified UI to highlight that the device being
+ * set up is a specific kind of device, and some extra permissions may be granted to the app
+ * as a result.
+ *
* @see AssociationRequest.Builder#setDeviceProfile
*/
public static final String DEVICE_PROFILE_WATCH =
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f3a4e1f79955..02e86cd4a863 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -370,6 +370,15 @@ public abstract class Context {
/*********** Hidden flags below this line ***********/
/**
+ * Flag for {@link #bindService}: This flag is only intended to be used by the system to
+ * indicate that a service binding is not considered as real package component usage and should
+ * not generate a {@link android.app.usage.UsageEvents.Event#APP_COMPONENT_USED} event in usage
+ * stats.
+ * @hide
+ */
+ public static final int BIND_NOT_APP_COMPONENT_USAGE = 0x00008000;
+
+ /**
* Flag for {@link #bindService}: allow the process hosting the target service to be treated
* as if it's as important as a perceptible app to the user and avoid the oom killer killing
* this process in low memory situations until there aren't any other processes left but the
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
index d70ac918e208..66295eb513d8 100644
--- a/core/java/android/content/pm/AppSearchPerson.java
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -42,7 +42,7 @@ public class AppSearchPerson extends GenericDocument {
public static final String KEY_IS_BOT = "isBot";
public static final String KEY_IS_IMPORTANT = "isImportant";
- private AppSearchPerson(@NonNull GenericDocument document) {
+ public AppSearchPerson(@NonNull GenericDocument document) {
super(document);
}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index ebe202b2a3fa..5af3b5a01d07 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -28,7 +28,6 @@ import android.content.LocusId;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -53,17 +52,23 @@ public class AppSearchShortcutInfo extends GenericDocument {
/** The name of the schema type for {@link ShortcutInfo} documents.*/
public static final String SCHEMA_TYPE = "Shortcut";
- public static final String KEY_PACKAGE_NAME = "packageName";
public static final String KEY_ACTIVITY = "activity";
- public static final String KEY_TITLE = "title";
- public static final String KEY_TEXT = "text";
+ public static final String KEY_SHORT_LABEL = "shortLabel";
+ public static final String KEY_SHORT_LABEL_RES_ID = "shortLabelResId";
+ public static final String KEY_SHORT_LABEL_RES_NAME = "shortLabelResName";
+ public static final String KEY_LONG_LABEL = "longLabel";
+ public static final String KEY_LONG_LABEL_RES_ID = "longLabelResId";
+ public static final String KEY_LONG_LABEL_RES_NAME = "longLabelResName";
public static final String KEY_DISABLED_MESSAGE = "disabledMessage";
+ public static final String KEY_DISABLED_MESSAGE_RES_ID = "disabledMessageResId";
+ public static final String KEY_DISABLED_MESSAGE_RES_NAME = "disabledMessageResName";
public static final String KEY_CATEGORIES = "categories";
public static final String KEY_INTENTS = "intents";
public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras";
public static final String KEY_PERSON = "person";
public static final String KEY_LOCUS_ID = "locusId";
public static final String KEY_RANK = "rank";
+ public static final String KEY_IMPLICIT_RANK = "implicitRank";
public static final String KEY_EXTRAS = "extras";
public static final String KEY_FLAGS = "flags";
public static final String KEY_ICON_RES_ID = "iconResId";
@@ -73,36 +78,62 @@ public class AppSearchShortcutInfo extends GenericDocument {
public static final String KEY_DISABLED_REASON = "disabledReason";
public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
- .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_PACKAGE_NAME)
- .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ACTIVITY)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
.build()
- ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ACTIVITY)
+ ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_SHORT_LABEL)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
- .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
- ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TITLE)
+ ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_SHORT_LABEL_RES_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
- .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
- ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TEXT)
+ ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_SHORT_LABEL_RES_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_LONG_LABEL)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
+ ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_LONG_LABEL_RES_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build()
+
+ ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_LONG_LABEL_RES_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_DISABLED_MESSAGE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
.build()
+ ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(
+ KEY_DISABLED_MESSAGE_RES_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build()
+
+ ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
+ KEY_DISABLED_MESSAGE_RES_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_CATEGORIES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
@@ -135,6 +166,10 @@ public class AppSearchShortcutInfo extends GenericDocument {
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
+ ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_IMPLICIT_RANK)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build()
+
).addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(KEY_EXTRAS)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
.build()
@@ -183,13 +218,21 @@ public class AppSearchShortcutInfo extends GenericDocument {
Objects.requireNonNull(shortcutInfo);
return new Builder(shortcutInfo.getId())
.setActivity(shortcutInfo.getActivity())
- .setPackageName(shortcutInfo.getPackage())
- .setTitle(shortcutInfo.getShortLabel())
- .setText(shortcutInfo.getLongLabel())
+ .setNamespace(shortcutInfo.getPackage())
+ .setShortLabel(shortcutInfo.getShortLabel())
+ .setShortLabelResId(shortcutInfo.getShortLabelResourceId())
+ .setShortLabelResName(shortcutInfo.getTitleResName())
+ .setLongLabel(shortcutInfo.getLongLabel())
+ .setLongLabelResId(shortcutInfo.getLongLabelResourceId())
+ .setLongLabelResName(shortcutInfo.getTextResName())
.setDisabledMessage(shortcutInfo.getDisabledMessage())
+ .setDisabledMessageResId(shortcutInfo.getDisabledMessageResourceId())
+ .setDisabledMessageResName(shortcutInfo.getDisabledMessageResName())
.setCategories(shortcutInfo.getCategories())
.setIntents(shortcutInfo.getIntents())
.setRank(shortcutInfo.getRank())
+ .setImplicitRank(shortcutInfo.getImplicitRank()
+ | (shortcutInfo.isRankChanged() ? ShortcutInfo.RANK_CHANGED_BIT : 0))
.setExtras(shortcutInfo.getExtras())
.setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp())
.setFlags(shortcutInfo.getFlags())
@@ -207,17 +250,8 @@ public class AppSearchShortcutInfo extends GenericDocument {
* @hide
*/
@NonNull
- public ShortcutInfo toShortcutInfo() {
- return toShortcutInfo(UserHandle.myUserId());
- }
-
- /**
- * @hide
- * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides.
- */
- @NonNull
- public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) {
- final String packageName = getPropertyString(KEY_PACKAGE_NAME);
+ public ShortcutInfo toShortcutInfo(@UserIdInt int userId) {
+ final String packageName = getNamespace();
final String activityString = getPropertyString(KEY_ACTIVITY);
final ComponentName activity = activityString == null
? null : ComponentName.unflattenFromString(activityString);
@@ -228,15 +262,24 @@ public class AppSearchShortcutInfo extends GenericDocument {
// @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with
// LauncherApps#getShortcutIconDrawable instead.
final Icon icon = null;
- final String title = getPropertyString(KEY_TITLE);
- final String text = getPropertyString(KEY_TEXT);
+ final String shortLabel = getPropertyString(KEY_SHORT_LABEL);
+ final int shortLabelResId = (int) getPropertyLong(KEY_SHORT_LABEL_RES_ID);
+ final String shortLabelResName = getPropertyString(KEY_SHORT_LABEL_RES_NAME);
+ final String longLabel = getPropertyString(KEY_LONG_LABEL);
+ final int longLabelResId = (int) getPropertyLong(KEY_LONG_LABEL_RES_ID);
+ final String longLabelResName = getPropertyString(KEY_LONG_LABEL_RES_NAME);
final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE);
+ final int disabledMessageResId = (int) getPropertyLong(KEY_DISABLED_MESSAGE_RES_ID);
+ final String disabledMessageResName = getPropertyString(KEY_DISABLED_MESSAGE_RES_NAME);
final String[] categories = getPropertyStringArray(KEY_CATEGORIES);
final Set<String> categoriesSet = categories == null
- ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories));
+ ? null : new ArraySet<>(Arrays.asList(categories));
final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS);
final Intent[] intents = intentsStrings == null
- ? null : Arrays.stream(intentsStrings).map(uri -> {
+ ? new Intent[0] : Arrays.stream(intentsStrings).map(uri -> {
+ if (TextUtils.isEmpty(uri)) {
+ return new Intent(Intent.ACTION_VIEW);
+ }
try {
return Intent.parseUri(uri, /* flags =*/ 0);
} catch (URISyntaxException e) {
@@ -251,15 +294,18 @@ public class AppSearchShortcutInfo extends GenericDocument {
if (intents != null) {
for (int i = 0; i < intents.length; i++) {
final Intent intent = intents[i];
- if (intent != null) {
- intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]);
+ if (intent == null || intentExtrases == null || intentExtrases.length <= i
+ || intentExtrases[i] == null || intentExtrases[i].size() == 0) {
+ continue;
}
+ intent.replaceExtras(intentExtrases[i]);
}
}
final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON));
final String locusIdString = getPropertyString(KEY_LOCUS_ID);
final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString);
final int rank = (int) getPropertyLong(KEY_RANK);
+ final int implicitRank = (int) getPropertyLong(KEY_IMPLICIT_RANK);
final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS);
final PersistableBundle extras = transformToPersistableBundle(extrasByte);
final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS));
@@ -268,12 +314,17 @@ public class AppSearchShortcutInfo extends GenericDocument {
final String iconUri = getPropertyString(KEY_ICON_URI);
final String bitmapPath = getPropertyString(KEY_BITMAP_PATH);
final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON);
- return new ShortcutInfo(
- userId, getUri(), packageName, activity, icon, title, 0, null,
- text, 0, null, disabledMessage, 0, null,
- categoriesSet, intents, rank, extras,
+ final ShortcutInfo si = new ShortcutInfo(
+ userId, getUri(), packageName, activity, icon, shortLabel, shortLabelResId,
+ shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
+ disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
disabledReason, persons, locusId, 0);
+ si.setImplicitRank(implicitRank);
+ if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
+ si.setRankChanged();
+ }
+ return si;
}
/** @hide */
@@ -310,9 +361,9 @@ public class AppSearchShortcutInfo extends GenericDocument {
* @hide
*/
@NonNull
- public Builder setTitle(@Nullable final CharSequence shortLabel) {
+ public Builder setShortLabel(@Nullable final CharSequence shortLabel) {
if (!TextUtils.isEmpty(shortLabel)) {
- setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty(
+ setPropertyString(KEY_SHORT_LABEL, Preconditions.checkStringNotEmpty(
shortLabel, "shortLabel cannot be empty").toString());
}
return this;
@@ -322,13 +373,50 @@ public class AppSearchShortcutInfo extends GenericDocument {
* @hide
*/
@NonNull
- public Builder setText(@Nullable final CharSequence longLabel) {
+ public Builder setShortLabelResId(@Nullable final int shortLabelResId) {
+ setPropertyLong(KEY_SHORT_LABEL_RES_ID, shortLabelResId);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setShortLabelResName(@Nullable final String shortLabelResName) {
+ if (!TextUtils.isEmpty(shortLabelResName)) {
+ setPropertyString(KEY_SHORT_LABEL_RES_NAME, shortLabelResName);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setLongLabel(@Nullable final CharSequence longLabel) {
if (!TextUtils.isEmpty(longLabel)) {
- setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty(
+ setPropertyString(KEY_LONG_LABEL, Preconditions.checkStringNotEmpty(
longLabel, "longLabel cannot be empty").toString());
}
return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setLongLabelResId(@Nullable final int longLabelResId) {
+ setPropertyLong(KEY_LONG_LABEL_RES_ID, longLabelResId);
+ return this;
+ }
+ /**
+ * @hide
+ */
+ public Builder setLongLabelResName(@Nullable final String longLabelResName) {
+ if (!TextUtils.isEmpty(longLabelResName)) {
+ setPropertyString(KEY_LONG_LABEL_RES_NAME, longLabelResName);
+ }
+ return this;
}
/**
@@ -347,6 +435,25 @@ public class AppSearchShortcutInfo extends GenericDocument {
* @hide
*/
@NonNull
+ public Builder setDisabledMessageResId(@Nullable final int disabledMessageResId) {
+ setPropertyLong(KEY_DISABLED_MESSAGE_RES_ID, disabledMessageResId);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setDisabledMessageResName(@Nullable final String disabledMessageResName) {
+ if (!TextUtils.isEmpty(disabledMessageResName)) {
+ setPropertyString(KEY_DISABLED_MESSAGE_RES_NAME, disabledMessageResName);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
public Builder setCategories(@Nullable final Set<String> categories) {
if (categories != null && !categories.isEmpty()) {
setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new));
@@ -384,9 +491,8 @@ public class AppSearchShortcutInfo extends GenericDocument {
intentExtrases[i] = extras == null
? new byte[0] : transformToByteArray(new PersistableBundle(extras));
}
-
- setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it ->
- it.toUri(0)).toArray(String[]::new));
+ setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it -> it.toUri(0))
+ .toArray(String[]::new));
setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases);
return this;
}
@@ -410,10 +516,14 @@ public class AppSearchShortcutInfo extends GenericDocument {
if (persons == null || persons.length == 0) {
return this;
}
- setPropertyDocument(KEY_PERSON,
- Arrays.stream(persons).map(person -> AppSearchPerson.instance(
- Objects.requireNonNull(person, "persons cannot contain null"))
- ).toArray(AppSearchPerson[]::new));
+ final GenericDocument[] documents = new GenericDocument[persons.length];
+ for (int i = 0; i < persons.length; i++) {
+ final Person person = persons[i];
+ if (person == null) continue;
+ final AppSearchPerson appSearchPerson = AppSearchPerson.instance(person);
+ documents[i] = appSearchPerson;
+ }
+ setPropertyDocument(KEY_PERSON, documents);
return this;
}
@@ -422,8 +532,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
*/
@NonNull
public Builder setRank(final int rank) {
- Preconditions.checkArgument((0 <= rank),
- "Rank cannot be negative or bigger than MAX_RANK");
+ Preconditions.checkArgument((0 <= rank), "Rank cannot be negative");
setPropertyLong(KEY_RANK, rank);
return this;
}
@@ -432,6 +541,15 @@ public class AppSearchShortcutInfo extends GenericDocument {
* @hide
*/
@NonNull
+ public Builder setImplicitRank(final int rank) {
+ setPropertyLong(KEY_IMPLICIT_RANK, rank);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
public Builder setExtras(@Nullable final PersistableBundle extras) {
if (extras != null) {
setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras));
@@ -444,7 +562,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
*/
public Builder setPackageName(@Nullable final String packageName) {
if (!TextUtils.isEmpty(packageName)) {
- setPropertyString(KEY_PACKAGE_NAME, packageName);
+ setNamespace(packageName);
}
return this;
}
@@ -579,7 +697,14 @@ public class AppSearchShortcutInfo extends GenericDocument {
@NonNull
private static Person[] parsePerson(@Nullable final GenericDocument[] persons) {
- return persons == null ? new Person[0] : Arrays.stream(persons).map(it ->
- ((AppSearchPerson) it).toPerson()).toArray(Person[]::new);
+ if (persons == null) return new Person[0];
+ final Person[] ret = new Person[persons.length];
+ for (int i = 0; i < persons.length; i++) {
+ final GenericDocument document = persons[i];
+ if (document == null) continue;
+ final AppSearchPerson person = new AppSearchPerson(document);
+ ret[i] = person.toPerson();
+ }
+ return ret;
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0aa1be94d279..1a5dad5f7596 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1205,7 +1205,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
CATEGORY_SOCIAL,
CATEGORY_NEWS,
CATEGORY_MAPS,
- CATEGORY_PRODUCTIVITY
+ CATEGORY_PRODUCTIVITY,
+ CATEGORY_ACCESSIBILITY
})
@Retention(RetentionPolicy.SOURCE)
public @interface Category {
@@ -1281,6 +1282,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public static final int CATEGORY_PRODUCTIVITY = 7;
/**
+ * Category for apps which are primarily accessibility apps, such as screen-readers.
+ *
+ * @see #category
+ */
+ public static final int CATEGORY_ACCESSIBILITY = 8;
+
+ /**
* Return a concise, localized title for the given
* {@link ApplicationInfo#category} value, or {@code null} for unknown
* values such as {@link #CATEGORY_UNDEFINED}.
@@ -1305,6 +1313,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return context.getText(com.android.internal.R.string.app_category_maps);
case ApplicationInfo.CATEGORY_PRODUCTIVITY:
return context.getText(com.android.internal.R.string.app_category_productivity);
+ case ApplicationInfo.CATEGORY_ACCESSIBILITY:
+ return context.getText(com.android.internal.R.string.app_category_accessibility);
default:
return null;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 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/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 5f80ba110773..275e81c0e868 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -68,7 +68,8 @@ public final class ShortcutInfo implements Parcelable {
private static final int IMPLICIT_RANK_MASK = 0x7fffffff;
- private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
+ /** @hide */
+ public static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK;
/** @hide */
public static final int RANK_NOT_SET = Integer.MAX_VALUE;
@@ -1595,6 +1596,9 @@ public final class ShortcutInfo implements Parcelable {
*/
@Nullable
public Intent[] getIntents() {
+ if (mIntents == null) {
+ return null;
+ }
final Intent[] ret = new Intent[mIntents.length];
for (int i = 0; i < ret.length; i++) {
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/devicestate/DeviceStateInfo.aidl b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl
new file mode 100644
index 000000000000..e85679243994
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.devicestate;
+
+parcelable DeviceStateInfo;
diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.java b/core/java/android/hardware/devicestate/DeviceStateInfo.java
new file mode 100644
index 000000000000..bc6af37afc45
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateInfo.java
@@ -0,0 +1,162 @@
+/*
+ * 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.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+
+/**
+ * Information about the state of the device.
+ *
+ * @hide
+ */
+public final class DeviceStateInfo implements Parcelable {
+ /** Bit that indicates the {@link #supportedStates} field has changed. */
+ public static final int CHANGED_SUPPORTED_STATES = 1 << 0;
+
+ /** Bit that indicates the {@link #baseState} field has changed. */
+ public static final int CHANGED_BASE_STATE = 1 << 1;
+
+ /** Bit that indicates the {@link #currentState} field has changed. */
+ public static final int CHANGED_CURRENT_STATE = 1 << 2;
+
+ @IntDef(prefix = {"CHANGED_"}, flag = true, value = {
+ CHANGED_SUPPORTED_STATES,
+ CHANGED_BASE_STATE,
+ CHANGED_CURRENT_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ChangeFlags {}
+
+ /**
+ * The list of states supported by the device.
+ */
+ @NonNull
+ public final int[] supportedStates;
+
+ /**
+ * The base (non-override) state of the device. The base state is the state of the device
+ * ignoring any override requests made through a call to {@link DeviceStateManager#requestState(
+ * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ public final int baseState;
+
+ /**
+ * The state of the device.
+ */
+ public final int currentState;
+
+ /**
+ * Creates a new instance of {@link DeviceStateInfo}.
+ * <p>
+ * NOTE: Unlike {@link #DeviceStateInfo(DeviceStateInfo)}, this constructor does not copy the
+ * supplied parameters.
+ */
+ public DeviceStateInfo(@NonNull int[] supportedStates, int baseState, int state) {
+ this.supportedStates = supportedStates;
+ this.baseState = baseState;
+ this.currentState = state;
+ }
+
+ /**
+ * Creates a new instance of {@link DeviceStateInfo} copying the fields of {@code info} into
+ * the fields of the returned instance.
+ */
+ public DeviceStateInfo(@NonNull DeviceStateInfo info) {
+ this(Arrays.copyOf(info.supportedStates, info.supportedStates.length),
+ info.baseState, info.currentState);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) return true;
+ if (other == null || getClass() != other.getClass()) return false;
+ DeviceStateInfo that = (DeviceStateInfo) other;
+ return baseState == that.baseState
+ && currentState == that.currentState
+ && Arrays.equals(supportedStates, that.supportedStates);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(baseState, currentState);
+ result = 31 * result + Arrays.hashCode(supportedStates);
+ return result;
+ }
+
+ /** Returns a bitmask of the differences between this instance and {@code other}. */
+ @ChangeFlags
+ public int diff(@NonNull DeviceStateInfo other) {
+ int diff = 0;
+ if (!Arrays.equals(supportedStates, other.supportedStates)) {
+ diff |= CHANGED_SUPPORTED_STATES;
+ }
+ if (baseState != other.baseState) {
+ diff |= CHANGED_BASE_STATE;
+ }
+ if (currentState != other.currentState) {
+ diff |= CHANGED_CURRENT_STATE;
+ }
+ return diff;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(supportedStates.length);
+ for (int i = 0; i < supportedStates.length; i++) {
+ dest.writeInt(supportedStates[i]);
+ }
+
+ dest.writeInt(baseState);
+ dest.writeInt(currentState);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<DeviceStateInfo>() {
+ @Override
+ public DeviceStateInfo createFromParcel(Parcel source) {
+ final int numberOfSupportedStates = source.readInt();
+ final int[] supportedStates = new int[numberOfSupportedStates];
+ for (int i = 0; i < numberOfSupportedStates; i++) {
+ supportedStates[i] = source.readInt();
+ }
+ final int baseState = source.readInt();
+ final int currentState = source.readInt();
+
+ return new DeviceStateInfo(supportedStates, baseState, currentState);
+ }
+
+ @Override
+ public DeviceStateInfo[] newArray(int size) {
+ return new DeviceStateInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 2d4b2ccd7514..250145e92b89 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -111,38 +111,63 @@ public final class DeviceStateManager {
}
/**
- * Registers a listener to receive notifications about changes in device state.
+ * Registers a callback to receive notifications about changes in device state.
*
* @param executor the executor to process notifications.
- * @param listener the listener to register.
+ * @param callback the callback to register.
*
- * @see DeviceStateListener
+ * @see DeviceStateCallback
*/
- public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
- @NonNull DeviceStateListener listener) {
- mGlobal.registerDeviceStateListener(listener, executor);
+ public void registerCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DeviceStateCallback callback) {
+ mGlobal.registerDeviceStateCallback(callback, executor);
}
/**
- * Unregisters a listener previously registered with
- * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
+ * Unregisters a callback previously registered with
+ * {@link #registerCallback(Executor, DeviceStateCallback)}.
*/
- public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
- mGlobal.unregisterDeviceStateListener(listener);
+ public void unregisterCallback(@NonNull DeviceStateCallback callback) {
+ mGlobal.unregisterDeviceStateCallback(callback);
}
- /**
- * Listens for changes in device states.
- */
- public interface DeviceStateListener {
+ /** Callback to receive notifications about changes in device state. */
+ public interface DeviceStateCallback {
+ /**
+ * Called in response to a change in the states supported by the device.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in the supported states.
+ *
+ * @param supportedStates the new supported states.
+ *
+ * @see DeviceStateManager#getSupportedStates()
+ */
+ default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
+
+ /**
+ * Called in response to a change in the base device state.
+ * <p>
+ * The base state is the state of the device without considering any requests made through
+ * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}
+ * from any client process. The base state is guaranteed to match the state provided with a
+ * call to {@link #onStateChanged(int)} when there are no active requests from any process.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in the non-override state.
+ *
+ * @param state the new base device state.
+ */
+ default void onBaseStateChanged(int state) {}
+
/**
* Called in response to device state changes.
* <p>
- * Guaranteed to be called once on registration of the listener with the
- * initial value and then on every subsequent change in device state.
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in device state.
*
- * @param deviceState the new device state.
+ * @param state the new device state.
*/
- void onDeviceStateChanged(int deviceState);
+ void onStateChanged(int state);
}
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index b9ae88ea840f..1b37fb9fdad3 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -19,7 +19,7 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -31,12 +31,14 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.Executor;
/**
* Provides communication with the device state system service on behalf of applications.
*
* @see DeviceStateManager
+ *
* @hide
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
@@ -68,13 +70,13 @@ public final class DeviceStateManagerGlobal {
private DeviceStateManagerCallback mCallback;
@GuardedBy("mLock")
- private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+ private final ArrayList<DeviceStateCallbackWrapper> mCallbacks = new ArrayList<>();
@GuardedBy("mLock")
private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
@Nullable
@GuardedBy("mLock")
- private Integer mLastReceivedState;
+ private DeviceStateInfo mLastReceivedInfo;
@VisibleForTesting
public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
@@ -87,18 +89,31 @@ public final class DeviceStateManagerGlobal {
* @see DeviceStateManager#getSupportedStates()
*/
public int[] getSupportedStates() {
- try {
- return mDeviceStateManager.getSupportedDeviceStates();
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
+ synchronized (mLock) {
+ final DeviceStateInfo currentInfo;
+ if (mLastReceivedInfo != null) {
+ // If we have mLastReceivedInfo a callback is registered for this instance and it
+ // is receiving the most recent info from the server. Use that info here.
+ currentInfo = mLastReceivedInfo;
+ } else {
+ // If mLastReceivedInfo is null there is no registered callback so we manually
+ // fetch the current info.
+ try {
+ currentInfo = mDeviceStateManager.getDeviceStateInfo();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ return Arrays.copyOf(currentInfo.supportedStates, currentInfo.supportedStates.length);
}
}
/**
* Submits a {@link DeviceStateRequest request} to modify the device state.
*
- * @see DeviceStateManager#requestState(DeviceStateRequest,
- * Executor, DeviceStateRequest.Callback)
+ * @see DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)
* @see DeviceStateRequest
*/
public void requestState(@NonNull DeviceStateRequest request,
@@ -157,49 +172,56 @@ public final class DeviceStateManagerGlobal {
}
/**
- * Registers a listener to receive notifications about changes in device state.
+ * Registers a callback to receive notifications about changes in device state.
*
- * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
+ * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
- public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
+ public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback,
@NonNull Executor executor) {
- Integer stateToReport;
- DeviceStateListenerWrapper wrapper;
+ DeviceStateCallbackWrapper wrapper;
+ DeviceStateInfo currentInfo;
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
- int index = findListenerLocked(listener);
+ int index = findCallbackLocked(callback);
if (index != -1) {
- // This listener is already registered.
+ // This callback is already registered.
return;
}
- wrapper = new DeviceStateListenerWrapper(listener, executor);
- mListeners.add(wrapper);
- stateToReport = mLastReceivedState;
- }
+ registerCallbackIfNeededLocked();
+ if (mLastReceivedInfo == null) {
+ // Initialize the last received info with the current info if this is the first
+ // callback being registered.
+ try {
+ mLastReceivedInfo = mDeviceStateManager.getDeviceStateInfo();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
- if (stateToReport != null) {
- // Notify the listener with the most recent device state from the server. If the state
- // to report is null this is likely the first listener added and we're still waiting
- // from the callback from the server.
- wrapper.notifyDeviceStateChanged(stateToReport);
+ currentInfo = new DeviceStateInfo(mLastReceivedInfo);
+
+ wrapper = new DeviceStateCallbackWrapper(callback, executor);
+ mCallbacks.add(wrapper);
}
+
+ wrapper.notifySupportedStatesChanged(currentInfo.supportedStates);
+ wrapper.notifyBaseStateChanged(currentInfo.baseState);
+ wrapper.notifyStateChanged(currentInfo.currentState);
}
/**
- * Unregisters a listener previously registered with
- * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+ * Unregisters a callback previously registered with
+ * {@link #registerDeviceStateCallback(DeviceStateCallback, Executor)}}.
*
- * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
+ * @see DeviceStateManager#unregisterCallback(DeviceStateCallback)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
- public void unregisterDeviceStateListener(DeviceStateListener listener) {
+ public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) {
synchronized (mLock) {
- int indexToRemove = findListenerLocked(listener);
+ int indexToRemove = findCallbackLocked(callback);
if (indexToRemove != -1) {
- mListeners.remove(indexToRemove);
+ mCallbacks.remove(indexToRemove);
}
}
}
@@ -210,14 +232,15 @@ public final class DeviceStateManagerGlobal {
try {
mDeviceStateManager.registerCallback(mCallback);
} catch (RemoteException ex) {
+ mCallback = null;
throw ex.rethrowFromSystemServer();
}
}
}
- private int findListenerLocked(DeviceStateListener listener) {
- for (int i = 0; i < mListeners.size(); i++) {
- if (mListeners.get(i).mDeviceStateListener.equals(listener)) {
+ private int findCallbackLocked(DeviceStateCallback callback) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).mDeviceStateCallback.equals(callback)) {
return i;
}
}
@@ -234,16 +257,34 @@ public final class DeviceStateManagerGlobal {
return null;
}
- /** Handles a call from the server that the device state has changed. */
- private void handleDeviceStateChanged(int newDeviceState) {
- ArrayList<DeviceStateListenerWrapper> listeners;
+ /** Handles a call from the server that the device state info has changed. */
+ private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
+ ArrayList<DeviceStateCallbackWrapper> callbacks;
+ DeviceStateInfo oldInfo;
synchronized (mLock) {
- mLastReceivedState = newDeviceState;
- listeners = new ArrayList<>(mListeners);
+ oldInfo = mLastReceivedInfo;
+ mLastReceivedInfo = info;
+ callbacks = new ArrayList<>(mCallbacks);
}
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).notifyDeviceStateChanged(newDeviceState);
+ final int diff = oldInfo == null ? 1 : info.diff(oldInfo);
+ if ((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0) {
+ for (int i = 0; i < callbacks.size(); i++) {
+ // Copy the array to prevent callbacks from modifying the internal state.
+ final int[] supportedStates = Arrays.copyOf(info.supportedStates,
+ info.supportedStates.length);
+ callbacks.get(i).notifySupportedStatesChanged(supportedStates);
+ }
+ }
+ if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).notifyBaseStateChanged(info.baseState);
+ }
+ }
+ if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).notifyStateChanged(info.currentState);
+ }
}
}
@@ -291,8 +332,8 @@ public final class DeviceStateManagerGlobal {
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
- public void onDeviceStateChanged(int deviceState) {
- handleDeviceStateChanged(deviceState);
+ public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+ handleDeviceStateInfoChanged(info);
}
@Override
@@ -311,17 +352,29 @@ public final class DeviceStateManagerGlobal {
}
}
- private static final class DeviceStateListenerWrapper {
- private final DeviceStateListener mDeviceStateListener;
+ private static final class DeviceStateCallbackWrapper {
+ @NonNull
+ private final DeviceStateCallback mDeviceStateCallback;
+ @NonNull
private final Executor mExecutor;
- DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) {
- mDeviceStateListener = listener;
+ DeviceStateCallbackWrapper(@NonNull DeviceStateCallback callback,
+ @NonNull Executor executor) {
+ mDeviceStateCallback = callback;
mExecutor = executor;
}
- void notifyDeviceStateChanged(int newDeviceState) {
- mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
+ void notifySupportedStatesChanged(int[] newSupportedStates) {
+ mExecutor.execute(() ->
+ mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
+ }
+
+ void notifyBaseStateChanged(int newBaseState) {
+ mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
+ }
+
+ void notifyStateChanged(int newDeviceState) {
+ mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState));
}
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
index 70f7002597ed..df488d2f6df1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateRequest.java
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -116,7 +116,7 @@ public final class DeviceStateRequest {
* requested state.
* <p>
* Guaranteed to be called after a call to
- * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+ * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} with a state
* matching the requested state.
*/
default void onRequestActivated(@NonNull DeviceStateRequest request) {}
@@ -125,7 +125,7 @@ public final class DeviceStateRequest {
* Called to indicate the request has been temporarily suspended.
* <p>
* Guaranteed to be called before a call to
- * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
*/
default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
@@ -135,7 +135,7 @@ public final class DeviceStateRequest {
* DeviceStateRequest.Callback)}.
* <p>
* Guaranteed to be called before a call to
- * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
* <p>
* Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
* occur before this method.
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 323ad21e4884..14ed03d09fd0 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -16,25 +16,26 @@
package android.hardware.devicestate;
+import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.IDeviceStateManagerCallback;
/** @hide */
interface IDeviceStateManager {
+ /** Returns the current device state info. */
+ DeviceStateInfo getDeviceStateInfo();
+
/**
* Registers a callback to receive notifications from the device state manager. Only one
* callback can be registered per-process.
* <p>
* As the callback mechanism is used to alert the caller of changes to request status a callback
* <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
- * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
+ * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown.
*
* @throws SecurityException if a callback is already registered for the calling process.
*/
void registerCallback(in IDeviceStateManagerCallback callback);
- /** Returns the array of supported device state identifiers. */
- int[] getSupportedDeviceStates();
-
/**
* Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index ee2a071741ef..593be867fe28 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -16,20 +16,23 @@
package android.hardware.devicestate;
+import android.hardware.devicestate.DeviceStateInfo;
+
/** @hide */
interface IDeviceStateManagerCallback {
/**
- * Called in response to a change in device state. Guaranteed to be called once with the initial
- * value on registration of the callback.
+ * Called in response to a change in {@link DeviceStateInfo}.
+ *
+ * @param info the new device state info.
*
- * @param deviceState the new state of the device.
+ * @see DeviceStateInfo
*/
- oneway void onDeviceStateChanged(int deviceState);
+ oneway void onDeviceStateInfoChanged(in DeviceStateInfo info);
/**
* Called to notify the callback that a request has become active. Guaranteed to be called
- * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
- * resulted in a device state change.
+ * after a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request
+ * becoming active resulted in a change of device state info.
*
* @param token the request token previously registered with
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
@@ -38,8 +41,8 @@ interface IDeviceStateManagerCallback {
/**
* Called to notify the callback that a request has become suspended. Guaranteed to be called
- * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
- * suspended resulted in a device state change.
+ * before a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request
+ * becoming suspended resulted in a change of device state info.
*
* @param token the request token previously registered with
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
@@ -49,8 +52,8 @@ interface IDeviceStateManagerCallback {
/**
* Called to notify the callback that a request has become canceled. No further callbacks will
* be triggered for this request. Guaranteed to be called before a subsequent call to
- * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
- * state change.
+ * {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request becoming canceled resulted
+ * in a change of device state info.
*
* @param token the request token previously registered with
* {@link IDeviceStateManager#requestState(IBinder, int, int)}
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/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/Build.java b/core/java/android/os/Build.java
index 74df1b2b9194..a5b0e8d149ef 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1288,10 +1288,12 @@ public class Build {
public static final String HOST = getString("ro.build.host");
/**
- * Returns true if we are running a debug build such as "user-debug" or "eng".
- * @hide
+ * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+ *
+ * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+ * application regardless of whether they have the "debuggable" attribute set, or downgrade
+ * selinux into "permissive" mode in particular.
*/
- @UnsupportedAppUsage
public static final boolean IS_DEBUGGABLE =
SystemProperties.getInt("ro.debuggable", 0) == 1;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
index c8e682c86ea7..e068772e954a 100644
--- a/core/java/android/os/CombinedVibrationEffect.java
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -76,7 +76,9 @@ public abstract class CombinedVibrationEffect implements Parcelable {
* A sequential vibration effect should be performed by multiple vibrators in order.
*
* @see CombinedVibrationEffect.SequentialCombination
+ * @hide
*/
+ @TestApi
@NonNull
public static SequentialCombination startSequential() {
return new SequentialCombination();
@@ -162,7 +164,9 @@ public abstract class CombinedVibrationEffect implements Parcelable {
* A combination of haptic effects that should be played in multiple vibrators in sequence.
*
* @see CombinedVibrationEffect#startSequential()
+ * @hide
*/
+ @TestApi
public static final class SequentialCombination {
private final ArrayList<CombinedVibrationEffect> mEffects = new ArrayList<>();
diff --git a/core/java/android/os/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..87dced8a3437 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -155,22 +155,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 +187,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 09d0af11a39f..85cef84d8d30 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10512,18 +10512,6 @@ public final class Settings {
"force_desktop_mode_on_external_displays";
/**
- * Whether to allow non-resizable apps to be freeform.
- *
- * TODO(b/176061101) remove after update all usages
- * @deprecated use {@link #DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}
- * @hide
- */
- @Deprecated
- @Readable
- public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
- "enable_sizecompat_freeform";
-
- /**
* Whether to allow non-resizable apps to be shown in multi-window. The app will be
* letterboxed if the request orientation is not met, and will be shown in size-compat
* mode if the container size has changed.
@@ -16576,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/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/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..09452828057e 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -383,7 +383,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;
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/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..ff84aa57a628 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -31,6 +31,7 @@ import static android.view.RemoteAnimationTargetProto.START_BOUNDS;
import static android.view.RemoteAnimationTargetProto.START_LEASH;
import static android.view.RemoteAnimationTargetProto.TASK_ID;
import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.IntDef;
import android.app.PictureInPictureParams;
@@ -195,12 +196,30 @@ public class RemoteAnimationTarget implements Parcelable {
*/
public PictureInPictureParams pictureInPictureParams;
+ /**
+ * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used
+ * for non-app window.
+ */
+ public final @WindowManager.LayoutParams.WindowType int windowType;
+
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
SurfaceControl startLeash, Rect startBounds,
PictureInPictureParams pictureInPictureParams) {
+ this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
+ position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
+ startBounds, pictureInPictureParams, INVALID_WINDOW_TYPE);
+ }
+
+ public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
+ Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+ Rect localBounds, Rect screenSpaceBounds,
+ WindowConfiguration windowConfig, boolean isNotInRecents,
+ SurfaceControl startLeash, Rect startBounds,
+ PictureInPictureParams pictureInPictureParams,
+ @WindowManager.LayoutParams.WindowType int windowType) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
@@ -217,25 +236,27 @@ public class RemoteAnimationTarget implements Parcelable {
this.startLeash = startLeash;
this.startBounds = startBounds == null ? null : new Rect(startBounds);
this.pictureInPictureParams = pictureInPictureParams;
+ this.windowType = windowType;
}
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);
+ windowType = in.readInt();
}
@Override
@@ -247,20 +268,21 @@ 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 */);
+ dest.writeInt(windowType);
}
public void dump(PrintWriter pw, String prefix) {
@@ -274,6 +296,7 @@ public class RemoteAnimationTarget implements Parcelable {
pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw);
pw.print(" screenSpaceBounds="); screenSpaceBounds.printShortString(pw);
pw.print(" localBounds="); localBounds.printShortString(pw);
+ pw.print(" windowType="); pw.print(windowType);
pw.println();
pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
pw.print(prefix); pw.print("leash="); pw.println(leash);
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/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/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..2cf50bbc6793 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -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 {
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/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/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_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 8d193bfa1dd2..0e0f98ec1fc4 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -861,6 +861,23 @@ static int android_media_AudioRecord_set_preferred_microphone_field_dimension(
return jStatus;
}
+static void android_media_AudioRecord_setLogSessionId(JNIEnv *env, jobject thiz,
+ jstring jlogSessionId) {
+ sp<AudioRecord> record = getAudioRecord(env, thiz);
+ if (record == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioRecord pointer for setLogSessionId()");
+ }
+ if (jlogSessionId == nullptr) {
+ ALOGV("%s: logSessionId nullptr", __func__);
+ record->setLogSessionId(nullptr);
+ return;
+ }
+ ScopedUtfChars logSessionId(env, jlogSessionId);
+ ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
+ record->setLogSessionId(logSessionId.c_str());
+}
+
// ----------------------------------------------------------------------------
static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
@@ -876,50 +893,48 @@ static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
- // name, signature, funcPtr
- {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
- {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
- (void *)android_media_AudioRecord_setup},
- {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
- {"native_release", "()V", (void *)android_media_AudioRecord_release},
- {"native_read_in_byte_array",
- "([BIIZ)I",
- (void *)android_media_AudioRecord_readInArray<jbyteArray>},
- {"native_read_in_short_array",
- "([SIIZ)I",
- (void *)android_media_AudioRecord_readInArray<jshortArray>},
- {"native_read_in_float_array",
- "([FIIZ)I",
- (void *)android_media_AudioRecord_readInArray<jfloatArray>},
- {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
- (void *)android_media_AudioRecord_readInDirectBuffer},
- {"native_get_buffer_size_in_frames",
- "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
- {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
- {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
- {"native_set_pos_update_period",
- "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
- {"native_get_pos_update_period",
- "()I", (void *)android_media_AudioRecord_get_pos_update_period},
- {"native_get_min_buff_size",
- "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
- {"native_getMetrics", "()Landroid/os/PersistableBundle;",
- (void *)android_media_AudioRecord_native_getMetrics},
- {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
- {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
- {"native_disableDeviceCallback", "()V",
- (void *)android_media_AudioRecord_disableDeviceCallback},
- {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
- (void *)android_media_AudioRecord_get_timestamp},
- {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
- (void *)android_media_AudioRecord_get_active_microphones},
- {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
- {"native_set_preferred_microphone_direction", "(I)I",
- (void *)android_media_AudioRecord_set_preferred_microphone_direction},
- {"native_set_preferred_microphone_field_dimension", "(F)I",
- (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
+ {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
+ {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
+ (void *)android_media_AudioRecord_setup},
+ {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
+ {"native_release", "()V", (void *)android_media_AudioRecord_release},
+ {"native_read_in_byte_array", "([BIIZ)I",
+ (void *)android_media_AudioRecord_readInArray<jbyteArray>},
+ {"native_read_in_short_array", "([SIIZ)I",
+ (void *)android_media_AudioRecord_readInArray<jshortArray>},
+ {"native_read_in_float_array", "([FIIZ)I",
+ (void *)android_media_AudioRecord_readInArray<jfloatArray>},
+ {"native_read_in_direct_buffer", "(Ljava/lang/Object;IZ)I",
+ (void *)android_media_AudioRecord_readInDirectBuffer},
+ {"native_get_buffer_size_in_frames", "()I",
+ (void *)android_media_AudioRecord_get_buffer_size_in_frames},
+ {"native_set_marker_pos", "(I)I", (void *)android_media_AudioRecord_set_marker_pos},
+ {"native_get_marker_pos", "()I", (void *)android_media_AudioRecord_get_marker_pos},
+ {"native_set_pos_update_period", "(I)I",
+ (void *)android_media_AudioRecord_set_pos_update_period},
+ {"native_get_pos_update_period", "()I",
+ (void *)android_media_AudioRecord_get_pos_update_period},
+ {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
+ {"native_getMetrics", "()Landroid/os/PersistableBundle;",
+ (void *)android_media_AudioRecord_native_getMetrics},
+ {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
+ {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
+ {"native_enableDeviceCallback", "()V",
+ (void *)android_media_AudioRecord_enableDeviceCallback},
+ {"native_disableDeviceCallback", "()V",
+ (void *)android_media_AudioRecord_disableDeviceCallback},
+ {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
+ (void *)android_media_AudioRecord_get_timestamp},
+ {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
+ (void *)android_media_AudioRecord_get_active_microphones},
+ {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
+ {"native_set_preferred_microphone_direction", "(I)I",
+ (void *)android_media_AudioRecord_set_preferred_microphone_direction},
+ {"native_set_preferred_microphone_field_dimension", "(F)I",
+ (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
+ {"native_setLogSessionId", "(Ljava/lang/String;)V",
+ (void *)android_media_AudioRecord_setLogSessionId},
};
// field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index da60a75ba900..cae6db57e99c 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1426,8 +1426,13 @@ static void android_media_AudioTrack_setLogSessionId(JNIEnv *env, jobject thiz,
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioTrack pointer for setLogSessionId()");
}
+ if (jlogSessionId == nullptr) {
+ ALOGV("%s: logSessionId nullptr", __func__);
+ track->setLogSessionId(nullptr);
+ return;
+ }
ScopedUtfChars logSessionId(env, jlogSessionId);
- ALOGV("%s: logSessionId %s", __func__, logSessionId.c_str());
+ ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
track->setLogSessionId(logSessionId.c_str());
}
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/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c580b345d85b..ad1d25208a9a 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -282,7 +282,8 @@ message GlobalSettingsProto {
optional SettingProto force_rtl = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto emulate_display_cutout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto force_desktop_mode_on_external_displays = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Deprecated, use enable_non_resizable_multi_window
+ optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ];
optional SettingProto enable_non_resizable_multi_window = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Development development = 39;
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_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..633e2161b5ee 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,15 +4668,6 @@
<!-- WindowsManager JetPack display features -->
<string name="config_display_features" translatable="false" />
- <!-- Physical Display IDs of the display-devices that are swapped when a folding device folds.
- This list is expected to contain two elements: the first is the display to use
- when the device is folded, the second is the display to use when unfolded. If the array
- is empty or the display IDs are not recognized, this feature is turned off and the value
- ignored.
- TODO: b/170470621 - remove once we can have multiple Internal displays in DMS as
- well as a notification from DisplayStateManager. -->
- <string-array name="config_internalFoldedPhysicalDisplayIds" translatable="false" />
-
<!-- Aspect ratio of task level letterboxing. Values <= 1.0 will be ignored.
Note: Activity min/max aspect ratio restrictions will still be respected by the
activity-level letterboxing (size-compat mode). Therefore this override can control the
@@ -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..9bc92e14de53 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3168,6 +3168,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..ff9d26fb2363 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -482,6 +482,8 @@
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
<java-symbol type="string" name="config_bandwidthEstimateSource" />
+ <java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" />
+ <java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -1893,6 +1895,7 @@
<java-symbol type="bool" name="config_notificationHeaderClickableForExpand" />
<java-symbol type="bool" name="config_enableNightMode" />
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
+ <java-symbol type="bool" name="config_tintNotificationsWithTheme" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
@@ -1948,6 +1951,7 @@
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
<java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" />
+ <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" />
<java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" />
@@ -2468,7 +2472,10 @@
<java-symbol type="string" name="config_keyguardComponent" />
<!-- Biometric messages -->
+ <java-symbol type="string" name="biometric_app_setting_name" />
+ <java-symbol type="string" name="biometric_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="biometric_dialog_default_title" />
+ <java-symbol type="string" name="biometric_dialog_default_subtitle" />
<java-symbol type="string" name="biometric_error_hw_unavailable" />
<java-symbol type="string" name="biometric_error_user_canceled" />
<java-symbol type="string" name="biometric_not_recognized" />
@@ -2476,6 +2483,10 @@
<java-symbol type="string" name="biometric_error_device_not_secured" />
<java-symbol type="string" name="biometric_error_generic" />
+ <!-- Device credential strings for BiometricManager -->
+ <java-symbol type="string" name="screen_lock_app_setting_name" />
+ <java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
+
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
<java-symbol type="string" name="fingerprint_error_hw_not_available" />
@@ -2493,6 +2504,8 @@
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
+ <java-symbol type="string" name="fingerprint_app_setting_name" />
+ <java-symbol type="string" name="fingerprint_or_screen_lock_app_setting_name" />
<java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
@@ -2540,6 +2553,9 @@
<java-symbol type="string" name="face_acquired_sensor_dirty" />
<java-symbol type="array" name="face_acquired_vendor" />
<java-symbol type="string" name="face_name_template" />
+ <java-symbol type="string" name="face_app_setting_name" />
+ <java-symbol type="string" name="face_or_screen_lock_app_setting_name" />
+ <java-symbol type="string" name="face_dialog_default_subtitle" />
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
@@ -2892,6 +2908,9 @@
<java-symbol type="id" name="header_text" />
<java-symbol type="id" name="header_text_secondary" />
<java-symbol type="id" name="expand_button" />
+ <java-symbol type="id" name="expand_button_pill" />
+ <java-symbol type="id" name="expand_button_number" />
+ <java-symbol type="id" name="expand_button_icon" />
<java-symbol type="id" name="alternate_expand_target" />
<java-symbol type="id" name="notification_header" />
<java-symbol type="id" name="notification_top_line" />
@@ -2912,7 +2931,6 @@
<java-symbol type="dimen" name="notification_header_background_height" />
<java-symbol type="dimen" name="notification_header_touchable_height" />
<java-symbol type="dimen" name="notification_header_expand_icon_size" />
- <java-symbol type="dimen" name="notification_expand_button_padding_top" />
<java-symbol type="dimen" name="notification_header_icon_size" />
<java-symbol type="dimen" name="notification_header_app_name_margin_start" />
<java-symbol type="dimen" name="notification_header_separating_margin" />
@@ -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,7 +4177,6 @@
<java-symbol type="dimen" name="default_background_blur_radius" />
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
- <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
index 3706e4b3d8e8..b0c1f25ad030 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.net.TetheringManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -141,7 +142,7 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase {
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ mIntentFilter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
mContext.registerReceiver(mWifiReceiver, mIntentFilter);
logv("Clear Wifi before we start the test.");
diff --git a/core/tests/coretests/src/android/app/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/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
index da92e69b6378..ffe93bc99cf5 100644
--- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -47,15 +47,16 @@ public class AppSearchShortcutInfoTest {
final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id)
.setActivity(activity)
- .setText(id)
+ .setLongLabel(id)
.setIconResName(shortcutIconResName)
.setIntent(shortcutIntent)
.setPerson(person)
.setCategories(categorySet)
.setFlags(ShortcutInfo.FLAG_LONG_LIVED)
.build()
- .toShortcutInfo();
+ .toShortcutInfo(0);
+ assertThat(shortcut.getUserId()).isEqualTo(0);
assertThat(shortcut.getId()).isEqualTo(id);
assertThat(shortcut.getShortLabel()).isEqualTo(id);
assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName);
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index eae41e37e5f3..7bc81cd2f928 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -26,6 +26,8 @@ import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static junit.framework.Assert.fail;
+
import android.graphics.fonts.FontStyle;
import android.os.LocaleList;
import android.text.FontConfig;
@@ -44,6 +46,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -221,9 +224,113 @@ public final class FontListParserTest {
.that(readFamily(serialized)).isEqualTo(expected);
}
+ @Test
+ public void invalidXml_unpaired_family() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc</font>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unpaired_font() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unpaired_axis() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " <axis tag=\"wght\" styleValue=\"0\" >"
+ + " </font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_family() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'"
+ + " <font index='0'>test.ttc</font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_font() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void invalidXml_unclosed_axis() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font index='0'>test.ttc"
+ + " <axis tag=\"wght\" styleValue=\"0\""
+ + " </font>"
+ + " </family>"
+ + "</familyset>";
+
+ try (InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ FontListParser.parse(is);
+ fail();
+ } catch (IOException | XmlPullParserException e) {
+ // pass
+ }
+ }
+
private FontConfig.FontFamily readFamily(String xml)
throws IOException, XmlPullParserException {
- StandardCharsets.UTF_8.name();
ByteArrayInputStream buffer = new ByteArrayInputStream(
xml.getBytes(StandardCharsets.UTF_8));
XmlPullParser parser = Xml.newPullParser();
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 89411902bb6b..c06405affc1b 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -20,6 +20,7 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
import org.junit.Test;
@@ -33,16 +34,16 @@ public class VibratorInfoTest {
@Test
public void testHasAmplitudeControl() {
assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl());
- assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS
- | VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL).hasAmplitudeControl());
+ assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS
+ | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl());
}
@Test
public void testHasCapabilities() {
- assertTrue(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
- .hasCapability(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS));
- assertFalse(createInfo(VibratorInfo.CAPABILITY_COMPOSE_EFFECTS)
- .hasCapability(VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL));
+ assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+ .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
+ assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS)
+ .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL));
}
@Test
@@ -59,7 +60,7 @@ public class VibratorInfoTest {
@Test
public void testIsPrimitiveSupported() {
- VibratorInfo info = new VibratorInfo(/* id= */ 0, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS,
null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -73,30 +74,30 @@ public class VibratorInfoTest {
@Test
public void testEquals() {
VibratorInfo empty = new VibratorInfo(1, 0, null, null);
- VibratorInfo complete = new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK});
assertEquals(complete, complete);
- assertEquals(complete, new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}));
assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
new int[]{VibrationEffect.EFFECT_CLICK},
new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{}, new int[]{})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})));
- assertFalse(complete.equals(new VibratorInfo(1, VibratorInfo.CAPABILITY_AMPLITUDE_CONTROL,
+ assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL,
new int[]{VibrationEffect.EFFECT_CLICK}, null)));
}
@Test
public void testSerialization() {
- VibratorInfo original = new VibratorInfo(1, VibratorInfo.CAPABILITY_COMPOSE_EFFECTS,
+ VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS,
new int[]{VibrationEffect.EFFECT_CLICK}, null);
Parcel parcel = Parcel.obtain();
diff --git a/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/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/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp
index f7b593264cda..7748de57c2bc 100644
--- a/core/tests/devicestatetests/Android.bp
+++ b/core/tests/devicestatetests/Android.bp
@@ -28,6 +28,7 @@ android_test {
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
+ "mockito-target-minus-junit4",
],
libs: ["android.test.runner"],
platform_apis: true,
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
new file mode 100644
index 000000000000..dcef0a7c316d
--- /dev/null
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.devicestate;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link DeviceStateInfo}.
+ * <p/>
+ * Run with <code>atest DeviceStateInfoTest</code>.
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public final class DeviceStateInfoTest {
+ @Test
+ public void create() {
+ final int[] supportedStates = new int[] { 0, 1, 2 };
+ final int baseState = 0;
+ final int currentState = 2;
+
+ final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+ assertNotNull(info.supportedStates);
+ assertEquals(supportedStates, info.supportedStates);
+ assertEquals(baseState, info.baseState);
+ assertEquals(currentState, info.currentState);
+ }
+
+ @Test
+ public void equals() {
+ final int[] supportedStates = new int[] { 0, 1, 2 };
+ final int baseState = 0;
+ final int currentState = 2;
+
+ final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+ assertTrue(info.equals(info));
+
+ final DeviceStateInfo sameInfo = new DeviceStateInfo(supportedStates, baseState,
+ currentState);
+ assertTrue(info.equals(sameInfo));
+
+ final DeviceStateInfo differentInfo = new DeviceStateInfo(new int[]{ 0, 2}, baseState,
+ currentState);
+ assertFalse(info.equals(differentInfo));
+ }
+
+ @Test
+ public void diff_sameObject() {
+ final int[] supportedStates = new int[] { 0, 1, 2 };
+ final int baseState = 0;
+ final int currentState = 2;
+
+ final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState);
+ assertEquals(0, info.diff(info));
+ }
+
+ @Test
+ public void diff_differentSupportedStates() {
+ final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 0);
+ final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 2 }, 0, 0);
+ final int diff = info.diff(otherInfo);
+ assertTrue((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+ assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+ assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+ }
+
+ @Test
+ public void diff_differentNonOverrideState() {
+ final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 1, 0);
+ final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 2, 0);
+ final int diff = info.diff(otherInfo);
+ assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+ assertTrue((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+ assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+ }
+
+ @Test
+ public void diff_differentState() {
+ final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 1);
+ final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 0, 2);
+ final int diff = info.diff(otherInfo);
+ assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0);
+ assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0);
+ assertTrue((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0);
+ }
+
+ @Test
+ public void writeToParcel() {
+ final int[] supportedStates = new int[] { 0, 1, 2 };
+ final int nonOverrideState = 0;
+ final int state = 2;
+ final DeviceStateInfo originalInfo =
+ new DeviceStateInfo(supportedStates, nonOverrideState, state);
+
+ final Parcel parcel = Parcel.obtain();
+ originalInfo.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ final DeviceStateInfo info = DeviceStateInfo.CREATOR.createFromParcel(parcel);
+ assertEquals(originalInfo, info);
+ }
+}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index e7fdfb8c19e9..79b4d8bc5f1c 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,15 @@
package android.hardware.devicestate;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import android.annotation.Nullable;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.IBinder;
import android.os.RemoteException;
@@ -32,6 +36,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.HashSet;
@@ -58,42 +63,75 @@ public final class DeviceStateManagerGlobalTest {
}
@Test
- public void registerListener() {
- mService.setBaseState(DEFAULT_DEVICE_STATE);
-
- TestDeviceStateListener listener1 = new TestDeviceStateListener();
- TestDeviceStateListener listener2 = new TestDeviceStateListener();
+ public void registerCallback() {
+ DeviceStateCallback callback1 = mock(DeviceStateCallback.class);
+ DeviceStateCallback callback2 = mock(DeviceStateCallback.class);
- mDeviceStateManagerGlobal.registerDeviceStateListener(listener1,
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1,
ConcurrentUtils.DIRECT_EXECUTOR);
- mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
- assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
+ // Verify initial callbacks
+ verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+ verify(callback1).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback1).onStateChanged(eq(mService.getMergedState()));
+ verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+ verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback2).onStateChanged(eq(mService.getMergedState()));
+
+ Mockito.reset(callback1);
+ Mockito.reset(callback2);
+
+ // Change the supported states and verify callback
+ mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
+ verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+ verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+ mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });
+
+ Mockito.reset(callback1);
+ Mockito.reset(callback2);
+
+ // Change the base state and verify callback
mService.setBaseState(OTHER_DEVICE_STATE);
- assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
- assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
+ verify(callback1).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback1).onStateChanged(eq(mService.getMergedState()));
+ verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback2).onStateChanged(eq(mService.getMergedState()));
+
+ Mockito.reset(callback1);
+ Mockito.reset(callback2);
+
+ // Change the requested state and verify callback
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ verify(callback1).onStateChanged(eq(mService.getMergedState()));
+ verify(callback2).onStateChanged(eq(mService.getMergedState()));
}
@Test
- public void unregisterListener() {
- mService.setBaseState(DEFAULT_DEVICE_STATE);
+ public void unregisterCallback() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
- TestDeviceStateListener listener = new TestDeviceStateListener();
-
- mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
- mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
+ // Verify initial callbacks
+ verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getMergedState()));
+ Mockito.reset(callback);
+
+ mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);
+ mService.setSupportedStates(new int[]{OTHER_DEVICE_STATE});
mService.setBaseState(OTHER_DEVICE_STATE);
- assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+ verifyZeroInteractions(callback);
}
@Test
- public void submittingRequestRegisteredCallback() {
+ public void submittingRequestRegistersCallback() {
assertTrue(mService.mCallbacks.isEmpty());
DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -104,37 +142,22 @@ public final class DeviceStateManagerGlobalTest {
@Test
public void submitRequest() {
- mService.setBaseState(DEFAULT_DEVICE_STATE);
-
- TestDeviceStateListener listener = new TestDeviceStateListener();
- mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ Mockito.reset(callback);
DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
- assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ Mockito.reset(callback);
mDeviceStateManagerGlobal.cancelRequest(request);
- assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
- }
-
- private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
- @Nullable
- private Integer mLastReportedDeviceState;
-
- @Override
- public void onDeviceStateChanged(int deviceState) {
- mLastReportedDeviceState = deviceState;
- }
-
- @Nullable
- public Integer getLastReportedState() {
- return mLastReportedDeviceState;
- }
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
}
private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
@@ -150,12 +173,34 @@ public final class DeviceStateManagerGlobalTest {
}
}
+ private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
private int mBaseState = DEFAULT_DEVICE_STATE;
- private int mMergedState = DEFAULT_DEVICE_STATE;
private ArrayList<Request> mRequests = new ArrayList<>();
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
+ private DeviceStateInfo getInfo() {
+ final int mergedState = mRequests.isEmpty()
+ ? mBaseState : mRequests.get(mRequests.size() - 1).state;
+ return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
+ }
+
+ private void notifyDeviceStateInfoChanged() {
+ final DeviceStateInfo info = getInfo();
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onDeviceStateInfoChanged(info);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ @Override
+ public DeviceStateInfo getDeviceStateInfo() {
+ return getInfo();
+ }
+
@Override
public void registerCallback(IDeviceStateManagerCallback callback) {
if (mCallbacks.contains(callback)) {
@@ -163,16 +208,6 @@ public final class DeviceStateManagerGlobalTest {
}
mCallbacks.add(callback);
- try {
- callback.onDeviceStateChanged(mMergedState);
- } catch (RemoteException e) {
- // Do nothing. Should never happen.
- }
- }
-
- @Override
- public int[] getSupportedDeviceStates() {
- return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
}
@Override
@@ -190,7 +225,7 @@ public final class DeviceStateManagerGlobalTest {
final Request request = new Request(token, state, flags);
mRequests.add(request);
- notifyStateChangedIfNeeded();
+ notifyDeviceStateInfoChanged();
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
@@ -223,32 +258,29 @@ public final class DeviceStateManagerGlobalTest {
// Do nothing. Should never happen.
}
}
- notifyStateChangedIfNeeded();
+ notifyDeviceStateInfoChanged();
+ }
+
+ public void setSupportedStates(int[] states) {
+ mSupportedStates = states;
+ notifyDeviceStateInfoChanged();
+ }
+
+ public int[] getSupportedStates() {
+ return mSupportedStates;
}
public void setBaseState(int state) {
mBaseState = state;
- notifyStateChangedIfNeeded();
+ notifyDeviceStateInfoChanged();
}
- private void notifyStateChangedIfNeeded() {
- final int originalMergedState = mMergedState;
-
- if (!mRequests.isEmpty()) {
- mMergedState = mRequests.get(mRequests.size() - 1).state;
- } else {
- mMergedState = mBaseState;
- }
+ public int getBaseState() {
+ return mBaseState;
+ }
- if (mMergedState != originalMergedState) {
- for (IDeviceStateManagerCallback callback : mCallbacks) {
- try {
- callback.onDeviceStateChanged(mMergedState);
- } catch (RemoteException e) {
- // Do nothing. Should never happen.
- }
- }
- }
+ public int getMergedState() {
+ return getInfo().currentState;
}
}
}
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.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.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/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/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 78ee1868eee7..31057f8d5fb8 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
@@ -132,8 +132,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/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..79ec624a1557 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -43,7 +43,6 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
@@ -125,7 +124,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void startSwipePipToHome_updatesOverrideMinSize() {
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
createPipParams(null));
@@ -153,7 +152,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskAppeared_updatesOverrideMinSize() {
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.onTaskAppeared(
createTaskInfo(mComponent1, createPipParams(null), minSize),
@@ -191,7 +190,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), null /* leash */);
- final Size minSize = new Size(100, 80);
+ final Size minSize = new Size(400, 320);
mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null), minSize));
diff --git a/libs/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/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/AudioRecord.java b/media/java/android/media/AudioRecord.java
index b19643761fd7..bf04b660425b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -275,6 +275,12 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
private AudioAttributes mAudioAttributes;
private boolean mIsSubmixFullVolume = false;
+ /**
+ * The log session id used for metrics.
+ * A null or empty string here means it is not set.
+ */
+ private String mLogSessionId;
+
//---------------------------------------------------------
// Constructor, Finalize
//--------------------
@@ -1913,6 +1919,25 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
return native_set_preferred_microphone_field_dimension(zoom) == AudioSystem.SUCCESS;
}
+ /**
+ * Sets a string handle to this AudioRecord for metrics collection.
+ *
+ * @param logSessionId a string which is used to identify this object
+ * to the metrics service. Proper generated Ids must be obtained
+ * from the Java metrics service and should be considered opaque.
+ * Use null to remove the logSessionId association.
+ * @throws IllegalStateException if AudioRecord not initialized.
+ *
+ * @hide
+ */
+ public void setLogSessionId(@Nullable String logSessionId) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw new IllegalStateException("AudioRecord not initialized");
+ }
+ native_setLogSessionId(logSessionId);
+ mLogSessionId = logSessionId;
+ }
+
//---------------------------------------------------------
// Interface definitions
//--------------------
@@ -2072,6 +2097,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
private native int native_set_preferred_microphone_direction(int direction);
private native int native_set_preferred_microphone_field_dimension(float zoom);
+ private native void native_setLogSessionId(@Nullable String logSessionId);
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 58174a0cf408..88731d25706c 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -567,6 +567,7 @@ public class AudioTrack extends PlayerBase
/**
* The log session id used for metrics.
+ * A null or empty string here means it is not set.
*/
private String mLogSessionId;
@@ -928,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
@@ -942,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");
@@ -3978,12 +3988,14 @@ public class AudioTrack extends PlayerBase
* Sets a string handle to this AudioTrack for metrics collection.
*
* @param logSessionId a string which is used to identify this object
- * to the metrics service.
+ * to the metrics service. Proper generated Ids must be obtained
+ * from the Java metrics service and should be considered opaque.
+ * Use null to remove the logSessionId association.
* @throws IllegalStateException if AudioTrack not initialized.
*
* @hide
*/
- public void setLogSessionId(@NonNull String logSessionId) {
+ public void setLogSessionId(@Nullable String logSessionId) {
if (mState == STATE_UNINITIALIZED) {
throw new IllegalStateException("track not initialized");
}
@@ -4226,7 +4238,7 @@ public class AudioTrack extends PlayerBase
private native int native_get_audio_description_mix_level_db(float[] level);
private native int native_set_dual_mono_mode(int dualMonoMode);
private native int native_get_dual_mono_mode(int[] dualMonoMode);
- private native void native_setLogSessionId(@NonNull String logSessionId);
+ private native void native_setLogSessionId(@Nullable String logSessionId);
/**
* Sets the audio service Player Interface Id.
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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1ce738ef065c..a0b952882162 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -551,9 +551,6 @@ class SettingsProtoDumpUtil {
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
GlobalSettingsProto.Development.FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
dumpSetting(s, p,
- Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
- GlobalSettingsProto.Development.ENABLE_SIZECOMPAT_FREEFORM);
- dumpSetting(s, p,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
GlobalSettingsProto.Development.ENABLE_NON_RESIZABLE_MULTI_WINDOW);
p.end(developmentToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 303e5bb7050d..4dc6d1475c4a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -227,7 +227,6 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
- Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV,
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_enroll.xml b/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
new file mode 100644
index 000000000000..9b5752d2de59
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Enrollment progress bar-->
+ <com.android.systemui.biometrics.UdfpsProgressBar
+ android:id="@+id/progress_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:max="100"
+ android:padding="@dimen/udfps_enroll_progress_thickness"
+ android:progress="0"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view.xml b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
index 380dd855ffe1..f32faa0df867 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view.xml
+++ b/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
@@ -14,8 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsAnimationView
+<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/udfps_animation_view"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent">
+</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
new file mode 100644
index 000000000000..644d1adac46b
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 6ae306e17209..e24c9e99a405 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,15 +22,10 @@
android:layout_height="match_parent"
systemui:sensorTouchAreaCoefficient="0.5">
- <!-- Enrollment progress bar-->
- <com.android.systemui.biometrics.UdfpsProgressBar
- android:id="@+id/progress_bar"
+ <com.android.systemui.biometrics.UdfpsSurfaceView
+ android:id="@+id/hbm_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:max="100"
- android:padding="@dimen/udfps_enroll_progress_thickness"
- android:progress="0"
- android:layout_gravity="center"
- android:visibility="gone"/>
+ android:visibility="invisible"/>
</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 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/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/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 87f6b8202ded..568fdea19219 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -16,6 +16,8 @@
package com.android.systemui.shared.system;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+
import android.app.PictureInPictureParams;
import android.app.WindowConfiguration;
import android.graphics.Point;
@@ -44,6 +46,9 @@ public class RemoteAnimationTargetCompat {
public static final int ACTIVITY_TYPE_ASSISTANT = WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
public final int activityType;
+ public static final int TYPE_NAVIGATION_BAR = WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+ public final int windowType;
+
public final int taskId;
public final SurfaceControlCompat leash;
public final boolean isTranslucent;
@@ -74,6 +79,7 @@ public class RemoteAnimationTargetCompat {
contentInsets = app.contentInsets;
activityType = app.windowConfiguration.getActivityType();
pictureInPictureParams = app.pictureInPictureParams;
+ windowType = app.windowType;
mStartLeash = app.startLeash;
}
@@ -114,6 +120,7 @@ public class RemoteAnimationTargetCompat {
activityType = ACTIVITY_TYPE_UNDEFINED;
}
pictureInPictureParams = null;
+ windowType = INVALID_WINDOW_TYPE;
mStartLeash = null;
}
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..a02900328ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
@@ -31,15 +31,18 @@ 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) {
@@ -60,6 +63,14 @@ public abstract class UdfpsAnimation extends Drawable {
mView = view;
}
+ boolean isIlluminationShowing() {
+ return mIlluminationShowing;
+ }
+
+ void setIlluminationShowing(boolean showing) {
+ mIlluminationShowing = showing;
+ }
+
/**
* @return The amount of padding that's needed on each side of the sensor, in pixels.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index 5290986b2a1c..28b57195c5d4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -58,11 +58,16 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
}
@Override
- void updateColor() {
+ protected void updateColor() {
mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
}
@Override
+ protected void onDestroy() {
+
+ }
+
+ @Override
public void onSensorRectUpdated(@NonNull RectF sensorRect) {
super.onSensorRectUpdated(sensorRect);
mSensorRect = sensorRect;
@@ -70,6 +75,10 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
final boolean isNightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
if (!isNightMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
index efc864ade5ff..ef7a34000841 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
@@ -34,12 +34,21 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation {
}
@Override
- void updateColor() {
+ protected void updateColor() {
+
+ }
+
+ @Override
+ protected void onDestroy() {
}
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
mFingerprintDrawable.draw(canvas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
index 8664e44c9ad2..5f268cfa8fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
@@ -42,6 +42,7 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
private static final String TAG = "UdfpsAnimationKeyguard";
@NonNull private final Context mContext;
+ @NonNull private final StatusBarStateController mStatusBarStateController;
private final int mMaxBurnInOffsetX;
private final int mMaxBurnInOffsetY;
@@ -54,6 +55,7 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
@NonNull StatusBarStateController statusBarStateController) {
super(context);
mContext = context;
+ mStatusBarStateController = statusBarStateController;
mMaxBurnInOffsetX = context.getResources()
.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
@@ -89,6 +91,10 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
@Override
public void draw(@NonNull Canvas canvas) {
+ if (isIlluminationShowing()) {
+ return;
+ }
+
canvas.save();
canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
mFingerprintDrawable.draw(canvas);
@@ -106,11 +112,16 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv
}
@Override
- public void updateColor() {
+ protected void updateColor() {
final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
final int ambientDisplayIconColor = Color.WHITE;
mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
ambientDisplayIconColor, mInterpolatedDarkAmount));
}
+
+ @Override
+ protected void onDestroy() {
+ mStatusBarStateController.removeCallback(this);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 44122cba8716..f4dd181eb329 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -22,41 +22,49 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.View;
+import android.widget.FrameLayout;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.statusbar.phone.StatusBar;
/**
- * Class that coordinates non-HBM animations (such as enroll, keyguard, BiometricPrompt,
- * FingerprintManager).
+ * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
+ * can support multiple child views drawing on the same region around the sensor location.
*/
-public class UdfpsAnimationView extends View implements DozeReceiver,
+public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
StatusBar.ExpansionChangedListener {
private static final String TAG = "UdfpsAnimationView";
+ @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
+
@NonNull private UdfpsView mParent;
- @Nullable private UdfpsAnimation mUdfpsAnimation;
@NonNull private RectF mSensorRect;
private int mAlpha;
public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
+ setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mUdfpsAnimation != null) {
+ if (getUdfpsAnimation() != null) {
final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
- mUdfpsAnimation.setAlpha(alpha);
- mUdfpsAnimation.draw(canvas);
+ getUdfpsAnimation().setAlpha(alpha);
+ getUdfpsAnimation().draw(canvas);
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getUdfpsAnimation().onDestroy();
+ }
+
private int expansionToAlpha(float expansion) {
// Fade to 0 opacity when reaching this expansion amount
final float maxExpansion = 0.4f;
@@ -69,38 +77,38 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
return (int) ((1 - percent) * 255);
}
- void setParent(@NonNull UdfpsView parent) {
- mParent = parent;
+ void onIlluminationStarting() {
+ getUdfpsAnimation().setIlluminationShowing(true);
+ postInvalidate();
}
- void setAnimation(@Nullable UdfpsAnimation animation) {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(null);
- }
+ void onIlluminationStopped() {
+ getUdfpsAnimation().setIlluminationShowing(false);
+ postInvalidate();
+ }
- mUdfpsAnimation = animation;
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.setAnimationView(this);
- }
+ void setParent(@NonNull UdfpsView parent) {
+ mParent = parent;
}
void onSensorRectUpdated(@NonNull RectF sensorRect) {
mSensorRect = sensorRect;
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.onSensorRectUpdated(mSensorRect);
+ if (getUdfpsAnimation() != null) {
+ getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
}
}
void updateColor() {
- if (mUdfpsAnimation != null) {
- mUdfpsAnimation.updateColor();
+ if (getUdfpsAnimation() != null) {
+ getUdfpsAnimation().updateColor();
}
+ postInvalidate();
}
@Override
public void dozeTimeTick() {
- if (mUdfpsAnimation instanceof DozeReceiver) {
- ((DozeReceiver) mUdfpsAnimation).dozeTimeTick();
+ if (getUdfpsAnimation() instanceof DozeReceiver) {
+ ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
}
}
@@ -111,16 +119,16 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
}
public int getPaddingX() {
- if (mUdfpsAnimation == null) {
+ if (getUdfpsAnimation() == null) {
return 0;
}
- return mUdfpsAnimation.getPaddingX();
+ return getUdfpsAnimation().getPaddingX();
}
public int getPaddingY() {
- if (mUdfpsAnimation == null) {
+ if (getUdfpsAnimation() == null) {
return 0;
}
- return mUdfpsAnimation.getPaddingY();
+ return getUdfpsAnimation().getPaddingY();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
new file mode 100644
index 000000000000..19e774937e20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
+ implements UdfpsEnrollHelper.Listener {
+
+ private static final String TAG = "UdfpsAnimationViewEnroll";
+
+ @NonNull private UdfpsAnimation mUdfpsAnimation;
+ @NonNull private UdfpsProgressBar mProgressBar;
+ @Nullable private UdfpsEnrollHelper mEnrollHelper;
+
+ @NonNull
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mUdfpsAnimation;
+ }
+
+ public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mUdfpsAnimation = new UdfpsAnimationEnroll(context);
+ }
+
+ public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
+ mEnrollHelper = helper;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mProgressBar = findViewById(R.id.progress_bar);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "Enroll helper is null");
+ return;
+ }
+
+ if (mEnrollHelper.shouldShowProgressBar()) {
+ mProgressBar.setVisibility(View.VISIBLE);
+
+ // Only need enrollment updates if the progress bar is showing :)
+ mEnrollHelper.setListener(this);
+ }
+ }
+
+ @Override
+ public void onEnrollmentProgress(int remaining, int totalSteps) {
+ final int interpolatedProgress = mProgressBar.getMax()
+ * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+ mProgressBar.setProgress(interpolatedProgress, true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
new file mode 100644
index 000000000000..3d2f5a0fe5cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Class that coordinates non-HBM animations during other usage of FingerprintManager (not
+ * including Keyguard).
+ */
+public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
+
+ private final UdfpsAnimationFpmOther mAnimation;
+
+ public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mAnimation = new UdfpsAnimationFpmOther(context);
+ }
+
+ @Nullable
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mAnimation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
new file mode 100644
index 000000000000..7d0b3e59feb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
+ @Nullable private UdfpsAnimationKeyguard mAnimation;
+
+ public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
+ if (mAnimation == null) {
+ mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
+ mAnimation.setAnimationView(this);
+ }
+ }
+
+ @Nullable
+ @Override
+ protected UdfpsAnimation getUdfpsAnimation() {
+ return mAnimation;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 71fba3302abc..c2efeeecd06c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -71,7 +71,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
- private final StatusBarStateController mStatusBarStateController;
+ @NonNull private final StatusBar mStatusBar;
+ @NonNull private final StatusBarStateController mStatusBarStateController;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -110,18 +111,20 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@Override
public void onEnrollmentProgress(int sensorId, int remaining) {
- if (mView == null) {
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "onEnrollProgress received but helper is null");
return;
}
- mView.onEnrollmentProgress(remaining);
+ mEnrollHelper.onEnrollmentProgress(remaining);
}
@Override
public void onEnrollmentHelp(int sensorId) {
- if (mView == null) {
+ if (mEnrollHelper == null) {
+ Log.e(TAG, "onEnrollmentHelp received but helper is null");
return;
}
- mView.onEnrollmentHelp();
+ mEnrollHelper.onEnrollmentHelp();
}
@Override
@@ -135,20 +138,14 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
@VisibleForTesting
final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
- (expansion, expanded) -> {
- if (mView != null) {
- mView.onExpansionChanged(expansion, expanded);
- }
- };
+ (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
@VisibleForTesting
final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
- if (mView != null) {
mView.onStateChanged(newState);
- }
}
};
@@ -189,7 +186,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @Nullable StatusBar statusBar) {
+ @NonNull StatusBar statusBar) {
mContext = context;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -197,6 +194,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
+ mStatusBar = statusBar;
mStatusBarStateController = statusBarStateController;
mSensorProps = findFirstUdfps();
@@ -217,9 +215,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
- statusBar.addExpansionChangedListener(mStatusBarExpansionListener);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
-
mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
}
@@ -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,34 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
});
}
- @Nullable
- private UdfpsAnimation getUdfpsAnimationForReason(int reason) {
+ @NonNull
+ private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
+
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+
switch (reason) {
case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
- return new UdfpsAnimationEnroll(mContext);
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
- return new UdfpsAnimationKeyguard(mContext, mStatusBarStateController);
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
- return new UdfpsAnimationFpmOther(mContext);
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
+ final UdfpsAnimationViewEnroll animation = (UdfpsAnimationViewEnroll)
+ inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
+ animation.setEnrollHelper(mEnrollHelper);
+ return animation;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
+ final UdfpsAnimationViewKeyguard animation = (UdfpsAnimationViewKeyguard)
+ inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
+ animation.setStatusBarStateController(mStatusBarStateController);
+ return animation;
+ }
+
+ case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
+ final UdfpsAnimationViewFpmOther animation = (UdfpsAnimationViewFpmOther)
+ inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
+ return animation;
+ }
+
default:
Log.d(TAG, "Animation for reason " + reason + " not supported yet");
return null;
@@ -371,6 +392,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "hideUdfpsOverlay | removing window");
// Reset the controller back to its starting state.
onFingerUp();
+
+ mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+
mWindowManager.removeView(mView);
mView = null;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 2442633a4a69..942fa7aae0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -16,22 +16,28 @@
package com.android.systemui.biometrics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import androidx.annotation.NonNull;
-
/**
* Helps keep track of enrollment state and animates the progress bar accordingly.
*/
public class UdfpsEnrollHelper {
private static final String TAG = "UdfpsEnrollHelper";
+ interface Listener {
+ void onEnrollmentProgress(int remaining, int totalSteps);
+ }
+
// IUdfpsOverlayController reason
private final int mEnrollReason;
private int mTotalSteps = -1;
private int mCurrentProgress = 0;
+ @Nullable Listener mListener;
+
public UdfpsEnrollHelper(int reason) {
mEnrollReason = reason;
}
@@ -40,21 +46,29 @@ public class UdfpsEnrollHelper {
return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
}
- void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+ void onEnrollmentProgress(int remaining) {
if (mTotalSteps == -1) {
mTotalSteps = remaining;
}
- mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
- / (mTotalSteps + 1);
- progressBar.setProgress(mCurrentProgress, true /* animate */);
+ if (mListener != null) {
+ mListener.onEnrollmentProgress(remaining, mTotalSteps);
+ }
}
- void updateProgress(@NonNull UdfpsProgressBar progressBar) {
- progressBar.setProgress(mCurrentProgress);
+ void onEnrollmentHelp() {
+
}
- void onEnrollmentHelp() {
+ void setListener(@NonNull Listener listener) {
+ mListener = listener;
+ // Only notify during setListener if enrollment is already in progress, so the progress
+ // bar can be updated. If enrollment has not started yet, the progress bar will be empty
+ // anyway.
+ if (mTotalSteps != -1) {
+ final int remainingSteps = mTotalSteps - mCurrentProgress;
+ mListener.onEnrollmentProgress(remainingSteps, mTotalSteps);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 97c215e1d75f..61ec127ee946 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -52,6 +52,12 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
+ // Make this SurfaceView draw on top of everything else in this window. This allows us to
+ // 1) Always show the HBM circle on top of everything else, and
+ // 2) Properly composite this view with any other animations in the same window no matter
+ // what contents are added in which order to this view hierarchy.
+ setZOrderOnTop(true);
+
mHolder = getHolder();
mHolder.setFormat(PixelFormat.RGBA_8888);
@@ -61,7 +67,9 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
mSensorPaint.setARGB(255, 255, 255, 255);
mSensorPaint.setStyle(Paint.Style.FILL);
- mIlluminationDotDrawable = canvas -> canvas.drawOval(mSensorRect, mSensorPaint);
+ mIlluminationDotDrawable = canvas -> {
+ canvas.drawOval(mSensorRect, mSensorPaint);
+ };
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 399794391700..cd849e63ba9c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -32,7 +32,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -51,12 +50,11 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private static final int DEBUG_TEXT_SIZE_PX = 32;
- @NonNull private final UdfpsSurfaceView mHbmSurfaceView;
- @NonNull private final UdfpsAnimationView mAnimationView;
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
- @Nullable private UdfpsProgressBar mProgressBar;
+ @NonNull private UdfpsSurfaceView mHbmSurfaceView;
+ @Nullable private UdfpsAnimationView mAnimationView;
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -66,7 +64,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
- @Nullable private UdfpsEnrollHelper mEnrollHelper;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -84,19 +81,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
a.recycle();
}
- // Inflate UdfpsSurfaceView
- final LayoutInflater inflater = LayoutInflater.from(context);
- mHbmSurfaceView = (UdfpsSurfaceView) inflater.inflate(R.layout.udfps_surface_view,
- null, false);
- addView(mHbmSurfaceView);
- mHbmSurfaceView.setVisibility(View.INVISIBLE);
-
- // Inflate UdfpsAnimationView
- mAnimationView = (UdfpsAnimationView) inflater.inflate(R.layout.udfps_animation_view,
- null, false);
- mAnimationView.setParent(this);
- addView(mAnimationView);
-
mSensorRect = new RectF();
mDebugTextPaint = new Paint();
@@ -107,22 +91,22 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
mIlluminationRequested = false;
}
+ @Override
+ protected void onFinishInflate() {
+ mHbmSurfaceView = findViewById(R.id.hbm_view);
+ }
+
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
mSensorProps = properties;
}
- void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
- mAnimationView.setAnimation(animation);
-
- mEnrollHelper = enrollHelper;
+ void setAnimationView(@NonNull UdfpsAnimationView animation) {
+ mAnimationView = animation;
+ animation.setParent(this);
- if (enrollHelper != null) {
- mEnrollHelper.updateProgress(mProgressBar);
- mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar()
- ? View.VISIBLE : View.GONE);
- } else {
- mProgressBar.setVisibility(View.GONE);
- }
+ // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
+ // after the animation type has been decided.
+ addView(animation, 0);
}
@Override
@@ -132,6 +116,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void dozeTimeTick() {
+ if (mAnimationView == null) {
+ return;
+ }
mAnimationView.dozeTimeTick();
}
@@ -143,12 +130,10 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void onExpansionChanged(float expansion, boolean expanded) {
mNotificationShadeExpanded = expanded;
- mAnimationView.onExpansionChanged(expansion, expanded);
- }
- @Override
- protected void onFinishInflate() {
- mProgressBar = findViewById(R.id.progress_bar);
+ if (mAnimationView != null) {
+ mAnimationView.onExpansionChanged(expansion, expanded);
+ }
}
@Override
@@ -229,7 +214,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
mIlluminationRequested = true;
- mAnimationView.setVisibility(View.INVISIBLE);
+ mAnimationView.onIlluminationStarting();
mHbmSurfaceView.setVisibility(View.VISIBLE);
mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
}
@@ -237,16 +222,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@Override
public void stopIllumination() {
mIlluminationRequested = false;
- mAnimationView.setVisibility(View.VISIBLE);
+ mAnimationView.onIlluminationStopped();
mHbmSurfaceView.setVisibility(View.INVISIBLE);
mHbmSurfaceView.stopIllumination();
}
-
- void onEnrollmentProgress(int remaining) {
- mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
- }
-
- void onEnrollmentHelp() {
-
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/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/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/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/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/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/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..52063285ac01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2052,8 +2052,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationLaunchHeight,
zProgress);
setTranslationZ(translationZ);
- float extraWidthForClipping = params.getWidth() - getWidth()
- + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
+ float extraWidthForClipping = params.getWidth() - getWidth();
setExtraWidthForClipping(extraWidthForClipping);
int top = params.getTop();
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 76917612b910..b0b91bd5177c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -22,6 +22,8 @@ import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -30,6 +32,7 @@ import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
@@ -1234,6 +1237,7 @@ public class NotificationContentView extends FrameLayout {
mCachedHeadsUpRemoteInput = null;
}
+
private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
boolean hasRemoteInput, PendingIntent existingPendingIntent,
RemoteInputView cachedView, NotificationViewWrapper wrapper) {
@@ -1271,6 +1275,15 @@ public class NotificationContentView extends FrameLayout {
if (color == Notification.COLOR_DEFAULT) {
color = mContext.getColor(R.color.default_remote_input_background);
}
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_tintNotificationsWithTheme)) {
+ Resources.Theme theme = new ContextThemeWrapper(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(
+ new int[]{com.android.internal.R.attr.colorAccent});
+ color = ta.getColor(0, color);
+ ta.recycle();
+ }
existing.setBackgroundColor(ContrastColorUtil.ensureTextBackgroundColor(color,
mContext.getColor(R.color.remote_input_text_enabled),
mContext.getColor(R.color.remote_input_hint)));
@@ -1342,12 +1355,10 @@ public class NotificationContentView extends FrameLayout {
&& isPersonWithShortcut
&& entry.getBubbleMetadata() != null;
if (showButton) {
- Drawable d = mContext.getResources().getDrawable(entry.isBubble()
+ // explicitly resolve drawable resource using SystemUI's theme
+ Drawable d = mContext.getDrawable(entry.isBubble()
? R.drawable.bubble_ic_stop_bubble
: R.drawable.bubble_ic_create_bubble);
- mContainingNotification.updateNotificationColor();
- final int tint = mContainingNotification.getNotificationColor();
- d.setTint(tint);
String contentDescription = mContext.getResources().getString(entry.isBubble()
? R.string.notification_conversation_unbubble
@@ -1381,9 +1392,8 @@ public class NotificationContentView extends FrameLayout {
return;
}
+ // explicitly resolve drawable resource using SystemUI's theme
Drawable snoozeDrawable = mContext.getDrawable(R.drawable.ic_snooze);
- mContainingNotification.updateNotificationColor();
- snoozeDrawable.setTint(mContainingNotification.getNotificationColor());
snoozeButton.setImageDrawable(snoozeDrawable);
final NotificationSnooze snoozeGuts = (NotificationSnooze) LayoutInflater.from(mContext)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 756fe6c5ba24..8446b4e6a3f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -133,7 +133,7 @@ public class AmbientState {
*/
public static int getNotificationLaunchHeight(Context context) {
int zDistance = getZDistanceBetweenElements(context);
- return getBaseHeight(zDistance) * 2;
+ return NOTIFICATIONS_HAVE_SHADOWS ? 2 * getBaseHeight(zDistance) : 4 * zDistance;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 3833637e8542..3739424b4f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -20,10 +20,12 @@ import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Pair;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -33,6 +35,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
@@ -103,6 +106,8 @@ public class NotificationChildrenContainer extends ViewGroup {
private ViewGroup mCurrentHeader;
private boolean mIsConversation;
+ private boolean mTintWithThemeAccent;
+ private boolean mShowGroupCountInExpander;
private boolean mShowDividersWhenExpanded;
private boolean mHideDividersDuringExpand;
private int mTranslationForHeader;
@@ -145,6 +150,10 @@ public class NotificationChildrenContainer extends ViewGroup {
com.android.internal.R.dimen.notification_content_margin);
mEnableShadowOnChildNotifications =
res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
+ mTintWithThemeAccent =
+ res.getBoolean(com.android.internal.R.bool.config_tintNotificationsWithTheme);
+ mShowGroupCountInExpander =
+ res.getBoolean(R.bool.config_showNotificationGroupCountInExpander);
mShowDividersWhenExpanded =
res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
mHideDividersDuringExpand =
@@ -229,7 +238,6 @@ public class NotificationChildrenContainer extends ViewGroup {
mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
}
if (mNotificationHeaderLowPriority != null) {
- headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
}
@@ -397,7 +405,20 @@ public class NotificationChildrenContainer extends ViewGroup {
mGroupingUtil.updateChildrenAppearance();
}
+ private void setExpandButtonNumber(NotificationViewWrapper wrapper) {
+ View expandButton = wrapper == null
+ ? null : wrapper.getExpandButton();
+ if (expandButton instanceof NotificationExpandButton) {
+ ((NotificationExpandButton) expandButton).setNumber(mUntruncatedChildCount);
+ }
+ }
+
public void updateGroupOverflow() {
+ if (mShowGroupCountInExpander) {
+ setExpandButtonNumber(mNotificationHeaderWrapper);
+ setExpandButtonNumber(mNotificationHeaderWrapperLowPriority);
+ return;
+ }
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
if (mUntruncatedChildCount > maxAllowedVisibleChildren) {
int number = mUntruncatedChildCount - maxAllowedVisibleChildren;
@@ -1201,8 +1222,21 @@ public class NotificationChildrenContainer extends ViewGroup {
}
public void onNotificationUpdated() {
- mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
- mContainingNotification.getNotificationColor());
+ if (mShowGroupCountInExpander) {
+ // The overflow number is not used, so its color is irrelevant; skip this
+ return;
+ }
+ int color = mContainingNotification.getNotificationColor();
+ if (mTintWithThemeAccent) {
+ // We're using the theme accent, color with the accent color instead of the notif color
+ Resources.Theme theme = new ContextThemeWrapper(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(
+ new int[]{com.android.internal.R.attr.colorAccent});
+ color = ta.getColor(0, color);
+ ta.recycle();
+ }
+ mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
}
public int getPositionInLinearLayout(View childInGroup) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/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..bf36435b78c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -78,7 +78,6 @@ import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -277,8 +276,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
- public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858)
- public static final boolean DEBUG_GESTURES_VERBOSE = true;
+ public static final boolean DEBUG_GESTURES = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
public static final boolean DEBUG_CAMERA_LIFT = false;
@@ -458,7 +456,9 @@ public class StatusBar extends SystemUI implements DemoMode,
private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
- private GestureRecorder mGestureRec = null;
+ private final GestureRecorder mGestureRec = DEBUG_GESTURES
+ ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
+ : null;
private final ScreenPinningRequest mScreenPinningRequest;
@@ -856,10 +856,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityIntentHelper = new ActivityIntentHelper(mContext);
DateTimeView.setReceiverHandler(timeTickHandler);
-
- if (DEBUG_GESTURES) {
- mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat");
- }
}
@Override
@@ -2271,7 +2267,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public boolean interceptTouchEvent(MotionEvent event) {
if (DEBUG_GESTURES) {
- if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) {
+ if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
event.getActionMasked(), (int) event.getX(), (int) event.getY(),
mDisabled1, mDisabled2);
@@ -2696,6 +2692,10 @@ public class StatusBar extends SystemUI implements DemoMode,
return mDisplay.getRotation();
}
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
boolean dismissShade, int flags) {
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
@@ -2721,7 +2721,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(flags);
int result = ActivityManager.START_CANCELED;
- ActivityOptions options = new ActivityOptions(getActivityOptions(
+ ActivityOptions options = new ActivityOptions(getActivityOptions(mDisplayId,
null /* remoteAnimation */));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
@@ -4366,6 +4366,7 @@ public class StatusBar extends SystemUI implements DemoMode,
executeActionDismissingKeyguard(() -> {
try {
intent.send(null, 0, null, null, null, null, getActivityOptions(
+ mDisplayId,
mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
@@ -4387,15 +4388,38 @@ public class StatusBar extends SystemUI implements DemoMode,
mMainThreadHandler.post(runnable);
}
- public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
+ /**
+ * Returns an ActivityOptions bundle created using the given parameters.
+ *
+ * @param displayId The ID of the display to launch the activity in. Typically this would be the
+ * display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
+ */
+ public static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter) {
return getDefaultActivityOptions(animationAdapter).toBundle();
}
- public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter,
- boolean isKeyguardShowing, long eventTime) {
+ /**
+ * Returns an ActivityOptions bundle created using the given parameters.
+ *
+ * @param displayId The ID of the display to launch the activity in. Typically this would be the
+ * display the status bar is on.
+ * @param animationAdapter The animation adapter used to start this activity, or {@code null}
+ * for the default animation.
+ * @param isKeyguardShowing Whether keyguard is currently showing.
+ * @param eventTime The event time in milliseconds since boot, not including sleep. See
+ * {@link ActivityOptions#setSourceInfo}.
+ */
+ public static Bundle getActivityOptions(int displayId,
+ @Nullable RemoteAnimationAdapter animationAdapter, boolean isKeyguardShowing,
+ long eventTime) {
ActivityOptions options = getDefaultActivityOptions(animationAdapter);
options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
: ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
+ options.setLaunchDisplayId(displayId);
+ options.setCallerDisplayId(displayId);
return options.toBundle();
}
@@ -4535,4 +4559,8 @@ public class StatusBar extends SystemUI implements DemoMode,
public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
mExpansionChangedListeners.add(listener);
}
+
+ public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
+ mExpansionChangedListeners.remove(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 598addc68d2e..34673f2503ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -427,8 +427,13 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
intent.getCreatorPackage(), adapter);
}
long eventTime = row.getAndResetLastActionUpTime();
- Bundle options = eventTime > 0 ? getActivityOptions(adapter,
- mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(adapter);
+ Bundle options = eventTime > 0
+ ? getActivityOptions(
+ mStatusBar.getDisplayId(),
+ adapter,
+ mKeyguardStateController.isShowing(),
+ eventTime)
+ : getActivityOptions(mStatusBar.getDisplayId(), adapter);
int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, options);
mMainThreadHandler.post(() -> {
@@ -450,6 +455,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
int launchResult = TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
+ mStatusBar.getDisplayId(),
mActivityLaunchAnimator.getLaunchAnimation(
row, mStatusBar.isOccluded())),
new UserHandle(UserHandle.getUserId(appUid)));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 38f3bc891394..59c1138431fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -94,15 +94,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
goingToFullShade,
oldState);
}
-
- @Override
- public void onDozeAmountChanged(float linearAmount, float amount) {
- if (DEBUG) {
- Log.d(TAG, String.format("onDozeAmountChanged: linearAmount=%f amount=%f",
- linearAmount, amount));
- }
- setDarkAmount(amount);
- }
};
@Inject
@@ -294,20 +285,6 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
}
}
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- *
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- private void setDarkAmount(float darkAmount) {
- boolean isAwake = darkAmount != 0;
- if (darkAmount == mDarkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
- }
-
private boolean isListAnimating() {
return mKeyguardVisibilityHelper.isVisibilityAnimating();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 8845a05cf6f5..5a80c05cc3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -205,7 +205,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
protected void onViewAttached() {
if (DEBUG) Log.d(TAG, "onViewAttached");
mAdapter.registerDataSetObserver(mDataSetObserver);
- mDataSetObserver.onChanged();
+ mAdapter.notifyDataSetChanged();
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mScreenLifecycle.addObserver(mScreenObserver);
@@ -373,14 +373,13 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
private void setDarkAmount(float darkAmount) {
- boolean isAwake = darkAmount != 0;
+ boolean isFullyDozed = darkAmount == 1;
if (darkAmount == mDarkAmount) {
return;
}
mDarkAmount = darkAmount;
mListView.setDarkAmount(darkAmount);
- mView.setVisibility(isAwake ? View.VISIBLE : View.GONE);
- if (!isAwake) {
+ if (isFullyDozed) {
closeSwitcherIfOpenAndNotSimple(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/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/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/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..a3a0cb402c76 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1330,7 +1330,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
deviceProfile, FgThread.getExecutor(), desc -> {
try {
- result.complete(desc);
+ result.complete(String.valueOf(desc));
} catch (Exception e) {
result.completeExceptionally(e);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 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..0b7dbdebce32 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));
+ public void onNat64PrefixEvent(final Nat64PrefixEventParcel event) {
+ mHandler.post(() -> handleNat64PrefixEvent(event.netId, event.prefixOperation,
+ event.prefixAddress, event.prefixLength));
}
@Override
- public void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
- int uid) {
- }
-
- @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;
@@ -5234,11 +5265,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 +5325,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 +6072,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 +7154,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 +7183,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 +7238,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 +7255,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 +7270,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 +7317,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 +7341,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 +7945,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 +7960,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
defaultNetworks.add(nai.network);
}
}
- return defaultNetworks.toArray(new Network[0]);
+ return defaultNetworks;
}
/**
@@ -7952,8 +7977,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 +8219,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 +8305,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 +8378,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 +8689,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 +8739,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 +9061,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("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", 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 +9093,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 +9167,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}.
*/
@@ -9197,7 +9251,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 +9288,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/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 e5ef9353135d..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(
@@ -3841,6 +3854,8 @@ public final class ActiveServices {
r.name.getClassName());
stopServiceAndUpdateAllowlistManagerLocked(r);
if (r.app.getThread() != null) {
+ // Bump the process to the top of LRU list
+ mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app.mServices, false);
try {
bumpServiceExecutingLocked(r, false, "destroy");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7cd494976c94..f409d713280a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -92,6 +92,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 +105,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;
@@ -2684,6 +2684,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode(), taskRoot);
+ if (event == Event.ACTIVITY_RESUMED) {
+ // Report component usage as an activity is an app component
+ mUsageStatsService.reportEvent(
+ activity.getPackageName(), userId, Event.APP_COMPONENT_USED);
+ }
}
ContentCaptureManagerInternal contentCaptureService = mContentCaptureService;
if (contentCaptureService != null && (event == Event.ACTIVITY_PAUSED
@@ -4963,7 +4968,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 +4986,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 +6104,10 @@ public class ActivityManagerService extends IActivityManager.Stub
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
+ // Report usage as process is persistent and being started.
+ mUsageStatsService.reportEvent(info.packageName, UserHandle.getUserId(app.uid),
+ Event.APP_COMPONENT_USED);
+
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
@@ -15179,8 +15188,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/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..06cacc70a9b8 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -29,6 +29,7 @@ import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.PendingIntent;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
@@ -52,6 +53,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.permission.IPermissionManager;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -1634,6 +1636,13 @@ public final class BroadcastQueue {
brOptions.getTemporaryAppAllowlistReason());
}
+ // Report that a component is used for explicit broadcasts.
+ if (!r.intent.isExcludingStopped() && r.curComponent != null
+ && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
+ mService.mUsageStatsService.reportEvent(
+ r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
+ }
+
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index f43c7f6278c9..2c8794d75795 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -32,6 +32,7 @@ import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.app.ContentProviderHolder;
import android.app.IApplicationThread;
+import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -57,6 +58,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -412,6 +414,12 @@ public class ContentProviderHelper {
final long origId = Binder.clearCallingIdentity();
try {
+ if (!TextUtils.equals(cpr.appInfo.packageName, callingPackage)) {
+ // Report component used since a content provider is being bound.
+ mService.mUsageStatsService.reportEvent(
+ cpr.appInfo.packageName, userId, Event.APP_COMPONENT_USED);
+ }
+
// Content provider is now in use, its package can't be stopped.
try {
checkTime(startTime,
diff --git a/services/core/java/com/android/server/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/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/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/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 b3a6f263953f..658d27f43313 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -25,6 +25,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -47,6 +48,7 @@ import com.android.server.policy.DeviceStatePolicyImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Optional;
/**
@@ -76,6 +78,9 @@ import java.util.Optional;
public final class DeviceStateManagerService extends SystemService {
private static final String TAG = "DeviceStateManagerService";
private static final boolean DEBUG = false;
+ // The device state to use as a placeholder before callback from the DeviceStateProvider occurs.
+ private static final DeviceState UNSPECIFIED_DEVICE_STATE =
+ new DeviceState(MINIMUM_DEVICE_STATE, "UNSPECIFIED");
private final Object mLock = new Object();
@NonNull
@@ -87,11 +92,11 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock")
private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
- // The current committed device state. The default of UNSET will be replaced by
- // the current state after the initial callback from the DeviceStateProvider.
+ // The current committed device state. The default of UNSPECIFIED_DEVICE_STATE will be replaced
+ // by the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
@NonNull
- private DeviceState mCommittedState = new DeviceState(MINIMUM_DEVICE_STATE, "UNSET");
+ private DeviceState mCommittedState = UNSPECIFIED_DEVICE_STATE;
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
@NonNull
@@ -103,7 +108,7 @@ public final class DeviceStateManagerService extends SystemService {
// The device state that is set by the device state provider.
@GuardedBy("mLock")
@NonNull
- private Optional<DeviceState> mBaseState = Optional.empty();
+ private DeviceState mBaseState = UNSPECIFIED_DEVICE_STATE;
// List of processes registered to receive notifications about changes to device state and
// request status indexed by process id.
@@ -166,7 +171,7 @@ public final class DeviceStateManagerService extends SystemService {
* @see #getOverrideState()
*/
@NonNull
- Optional<DeviceState> getBaseState() {
+ DeviceState getBaseState() {
synchronized (mLock) {
return mBaseState;
}
@@ -203,12 +208,26 @@ public final class DeviceStateManagerService extends SystemService {
/** Returns the list of currently supported device state identifiers. */
private int[] getSupportedStateIdentifiers() {
synchronized (mLock) {
- int[] supportedStates = new int[mDeviceStates.size()];
- for (int i = 0; i < supportedStates.length; i++) {
- supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
- }
- return supportedStates;
+ return getSupportedStateIdentifiersLocked();
+ }
+ }
+
+ /** Returns the list of currently supported device state identifiers. */
+ private int[] getSupportedStateIdentifiersLocked() {
+ int[] supportedStates = new int[mDeviceStates.size()];
+ for (int i = 0; i < supportedStates.length; i++) {
+ supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
}
+ return supportedStates;
+ }
+
+ @NonNull
+ private DeviceStateInfo getDeviceStateInfoLocked() {
+ final int[] supportedStates = getSupportedStateIdentifiersLocked();
+ final int baseState = mBaseState.getIdentifier();
+ final int currentState = mCommittedState.getIdentifier();
+
+ return new DeviceStateInfo(supportedStates, baseState, currentState);
}
@VisibleForTesting
@@ -217,19 +236,19 @@ public final class DeviceStateManagerService extends SystemService {
}
private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
+ boolean updatedPendingState;
synchronized (mLock) {
+ final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
+
mDeviceStates.clear();
for (int i = 0; i < supportedDeviceStates.length; i++) {
DeviceState state = supportedDeviceStates[i];
mDeviceStates.put(state.getIdentifier(), state);
}
- if (mBaseState.isPresent()
- && !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
- // The current base state is no longer valid. We'll clear it here, though
- // we won't actually update the current state until a callback comes from the
- // provider with the most recent state.
- mBaseState = Optional.empty();
+ final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
+ if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
+ return;
}
final int requestSize = mRequestRecords.size();
@@ -240,7 +259,14 @@ public final class DeviceStateManagerService extends SystemService {
}
}
- updatePendingStateLocked();
+ updatedPendingState = updatePendingStateLocked();
+ }
+
+ if (!updatedPendingState) {
+ // If the change in the supported states didn't result in a change of the pending state
+ // commitPendingState() will never be called and the callbacks will never be notified
+ // of the change.
+ notifyDeviceStateInfoChanged();
}
notifyRequestsOfStatusChangeIfNeeded();
@@ -265,23 +291,25 @@ public final class DeviceStateManagerService extends SystemService {
}
/**
- * Requests to set the base state. The request may not be honored under certain conditions, for
- * example if the provided state is not supported.
+ * Sets the base state.
+ *
+ * @throws IllegalArgumentException if the {@code identifier} is not a supported state.
*
* @see #isSupportedStateLocked(int)
*/
private void setBaseState(int identifier) {
+ boolean updatedPendingState;
synchronized (mLock) {
- if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
- // Base state hasn't changed. Nothing to do.
- return;
- }
-
- final Optional<DeviceState> baseState = getStateLocked(identifier);
- if (!baseState.isPresent()) {
+ final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
+ if (!baseStateOptional.isPresent()) {
throw new IllegalArgumentException("Base state is not supported");
}
+ final DeviceState baseState = baseStateOptional.get();
+ if (mBaseState.equals(baseState)) {
+ // Base state hasn't changed. Nothing to do.
+ return;
+ }
mBaseState = baseState;
final int requestSize = mRequestRecords.size();
@@ -292,7 +320,14 @@ public final class DeviceStateManagerService extends SystemService {
}
}
- updatePendingStateLocked();
+ updatedPendingState = updatePendingStateLocked();
+ }
+
+ if (!updatedPendingState) {
+ // If the change in base state didn't result in a change of the pending state
+ // commitPendingState() will never be called and the callbacks will never be notified
+ // of the change.
+ notifyDeviceStateInfoChanged();
}
notifyRequestsOfStatusChangeIfNeeded();
@@ -303,32 +338,39 @@ public final class DeviceStateManagerService extends SystemService {
* Tries to update the current pending state with the current requested state. Must call
* {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
* changed.
+ *
+ * @return {@code true} if the pending state has changed as a result of this call, {@code false}
+ * otherwise.
*/
- private void updatePendingStateLocked() {
+ private boolean updatePendingStateLocked() {
if (mPendingState.isPresent()) {
// Have pending state, can not configure a new state until the state is committed.
- return;
+ return false;
}
final DeviceState stateToConfigure;
if (!mRequestRecords.isEmpty()) {
stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
+ } else if (isSupportedStateLocked(mBaseState.getIdentifier())) {
+ // Base state could have recently become unsupported after a change in supported states.
+ stateToConfigure = mBaseState;
} else {
- stateToConfigure = mBaseState.orElse(null);
+ stateToConfigure = null;
}
if (stateToConfigure == null) {
// No currently requested state.
- return;
+ return false;
}
- if (stateToConfigure == mCommittedState) {
+ if (stateToConfigure.equals(mCommittedState)) {
// The state requesting to be committed already matches the current committed state.
- return;
+ return false;
}
mPendingState = Optional.of(stateToConfigure);
mIsPolicyWaitingForState = true;
+ return true;
}
/**
@@ -374,18 +416,22 @@ public final class DeviceStateManagerService extends SystemService {
*/
private void commitPendingState() {
// Update the current state.
- int newState;
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Committing state: " + mPendingState);
}
mCommittedState = mPendingState.get();
- newState = mCommittedState.getIdentifier();
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();
@@ -393,7 +439,7 @@ public final class DeviceStateManagerService extends SystemService {
}
// Notify callbacks of a change.
- notifyDeviceStateChanged(newState);
+ notifyDeviceStateInfoChanged();
// Notify the top request that it's active.
notifyRequestsOfStatusChangeIfNeeded();
@@ -402,14 +448,15 @@ public final class DeviceStateManagerService extends SystemService {
notifyPolicyIfNeeded();
}
- private void notifyDeviceStateChanged(int deviceState) {
+ private void notifyDeviceStateInfoChanged() {
if (Thread.holdsLock(mLock)) {
throw new IllegalStateException(
"Attempting to notify callbacks with service lock held.");
}
- // Grab the lock and copy the process records.
+ // Grab the lock and copy the process records and the current info.
ArrayList<ProcessRecord> registeredProcesses;
+ DeviceStateInfo info;
synchronized (mLock) {
if (mProcessRecords.size() == 0) {
return;
@@ -419,11 +466,13 @@ public final class DeviceStateManagerService extends SystemService {
for (int i = 0; i < mProcessRecords.size(); i++) {
registeredProcesses.add(mProcessRecords.valueAt(i));
}
+
+ info = getDeviceStateInfoLocked();
}
// After releasing the lock, send the notifications out.
for (int i = 0; i < registeredProcesses.size(); i++) {
- registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
+ registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
}
}
@@ -454,28 +503,20 @@ public final class DeviceStateManagerService extends SystemService {
}
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
- int currentState;
- ProcessRecord record;
- // Grab the lock to register the callback and get the current state.
synchronized (mLock) {
if (mProcessRecords.contains(pid)) {
throw new SecurityException("The calling process has already registered an"
+ " IDeviceStateManagerCallback.");
}
- record = new ProcessRecord(callback, pid);
+ ProcessRecord record = new ProcessRecord(callback, pid);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
-
mProcessRecords.put(pid, record);
- currentState = mCommittedState.getIdentifier();
}
-
- // Notify the callback of the state at registration.
- record.notifyDeviceStateAsync(currentState);
}
private void handleProcessDied(ProcessRecord processRecord) {
@@ -528,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();
@@ -626,9 +670,9 @@ public final class DeviceStateManagerService extends SystemService {
handleProcessDied(this);
}
- public void notifyDeviceStateAsync(int devicestate) {
+ public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
try {
- mCallback.onDeviceStateChanged(devicestate);
+ mCallback.onDeviceStateInfoChanged(info);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
ex);
@@ -752,6 +796,13 @@ public final class DeviceStateManagerService extends SystemService {
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
@Override // Binder call
+ public DeviceStateInfo getDeviceStateInfo() {
+ synchronized (mLock) {
+ return getDeviceStateInfoLocked();
+ }
+ }
+
+ @Override // Binder call
public void registerCallback(IDeviceStateManagerCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("Device state callback must not be null.");
@@ -767,16 +818,6 @@ public final class DeviceStateManagerService extends SystemService {
}
@Override // Binder call
- public int[] getSupportedDeviceStates() {
- final long token = Binder.clearCallingIdentity();
- try {
- return getSupportedStateIdentifiers();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override // Binder call
public void requestState(IBinder token, int state, int flags) {
getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
"Permission required to request device state.");
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 6cc55a6c4774..f3466006bd30 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -64,13 +64,13 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
private void printState(PrintWriter pw) {
DeviceState committedState = mService.getCommittedState();
- Optional<DeviceState> baseState = mService.getBaseState();
+ DeviceState baseState = mService.getBaseState();
Optional<DeviceState> overrideState = mService.getOverrideState();
pw.println("Committed state: " + committedState);
if (overrideState.isPresent()) {
pw.println("----------------------");
- pw.println("Base state: " + baseState.orElse(null));
+ pw.println("Base state: " + baseState);
pw.println("Override state: " + overrideState.get());
}
}
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 c62dd72ada70..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) {
@@ -532,7 +532,7 @@ public final class DisplayManagerService extends SystemService {
DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+ deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
new DeviceStateListener());
scheduleTraversalLocked(false);
@@ -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();
}
@@ -2974,9 +2976,9 @@ public final class DisplayManagerService extends SystemService {
/**
* Listens to changes in device state and reports the state to LogicalDisplayMapper.
*/
- class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
+ class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
@Override
- public void onDeviceStateChanged(int deviceState) {
+ public void onStateChanged(int deviceState) {
synchronized (mSyncRoot) {
mLogicalDisplayMapper.setDeviceStateLocked(deviceState);
}
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/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/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..7da53b50d927 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());
}
}
@@ -26117,6 +26129,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..a377f1c72375 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -362,6 +362,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 +499,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 +669,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 +1169,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 +1189,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 +1584,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) {
@@ -1947,7 +1964,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);
@@ -1975,59 +1992,57 @@ 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();
@@ -3114,7 +3129,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 +3141,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 +3504,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/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 82fc22c51afc..0e12584f80a1 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,7 +74,7 @@ class DisplayFoldController {
mHandler = handler;
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
- deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+ deviceStateManager.registerCallback(new HandlerExecutor(handler),
new DeviceStateListener(context));
}
@@ -208,7 +208,7 @@ class DisplayFoldController {
* matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState}
* resource.
*/
- private class DeviceStateListener implements DeviceStateManager.DeviceStateListener {
+ private class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
private final int[] mFoldedDeviceStates;
DeviceStateListener(Context context) {
@@ -217,7 +217,7 @@ class DisplayFoldController {
}
@Override
- public void onDeviceStateChanged(int deviceState) {
+ public void onStateChanged(int deviceState) {
boolean folded = false;
for (int i = 0; i < mFoldedDeviceStates.length; i++) {
if (deviceState == mFoldedDeviceStates[i]) {
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..9409eb5d1ad9 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -53,11 +53,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService}
@@ -72,6 +74,7 @@ public final class StorageUserConnection {
private final Context mContext;
private final int mUserId;
private final StorageSessionController mSessionController;
+ private final StorageManagerInternal mSmInternal;
private final ActiveConnection mActiveConnection = new ActiveConnection();
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
@GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>();
@@ -81,6 +84,7 @@ public final class StorageUserConnection {
mContext = Objects.requireNonNull(context);
mUserId = Preconditions.checkArgumentNonnegative(userId);
mSessionController = controller;
+ mSmInternal = LocalServices.getService(StorageManagerInternal.class);
mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
mHandlerThread.start();
}
@@ -152,9 +156,13 @@ public final class StorageUserConnection {
*/
public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
throws ExternalStorageServiceException {
+ List<String> primarySessionIds = mSmInternal.getPrimaryVolumeIds();
synchronized (mSessionsLock) {
for (String sessionId : mSessions.keySet()) {
- mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+ if (primarySessionIds.contains(sessionId)) {
+ mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+ return;
+ }
}
}
}
@@ -201,8 +209,7 @@ public final class StorageUserConnection {
return;
}
}
- StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
- sm.resetUser(mUserId);
+ mSmInternal.resetUser(mUserId);
}
/**
@@ -317,6 +324,23 @@ public final class StorageUserConnection {
}
}
+ private void asyncBestEffort(Consumer<IExternalStorageService> consumer) {
+ synchronized (mLock) {
+ if (mRemoteFuture == null) {
+ Slog.w(TAG, "Dropping async request service is not bound");
+ return;
+ }
+
+ IExternalStorageService service = mRemoteFuture.getNow(null);
+ if (service == null) {
+ Slog.w(TAG, "Dropping async request service is not connected");
+ return;
+ }
+
+ consumer.accept(service);
+ }
+ }
+
private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<Void> opFuture = new CompletableFuture<>();
RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
@@ -401,13 +425,13 @@ public final class StorageUserConnection {
public void notifyAnrDelayStarted(String packgeName, int uid, int tid, int reason)
throws ExternalStorageServiceException {
- try {
- waitForAsyncVoid((service, callback) ->
- service.notifyAnrDelayStarted(packgeName, uid, tid, reason, callback));
- } catch (Exception e) {
- throw new ExternalStorageServiceException("Failed to notify ANR delay started: "
- + packgeName, e);
- }
+ asyncBestEffort(service -> {
+ try {
+ service.notifyAnrDelayStarted(packgeName, uid, tid, reason);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify ANR delay started", e);
+ }
+ });
}
private void setResult(Bundle result, CompletableFuture<Void> future) {
diff --git a/services/core/java/com/android/server/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/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/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/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 151895f02ac4..c830ba9b61dd 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -808,7 +808,7 @@ class ActivityClientController extends IActivityClientController.Stub {
if (rootTask.inFreeformWindowingMode()) {
rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- } else if (!mService.mSizeCompatFreeform && r.inSizeCompatMode()) {
+ } else if (!mService.mSupportsNonResizableMultiWindow && r.inSizeCompatMode()) {
throw new IllegalStateException("Size-compat windows are currently not"
+ "freeform-enabled");
} else if (rootTask.getParent().inFreeformWindowingMode()) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f40f4a98964a..77c369f72323 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;
@@ -215,6 +214,7 @@ import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SO
import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -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;
@@ -2562,9 +2595,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* root task.
*/
boolean supportsFreeform() {
- return mAtmService.mSupportsFreeformWindowManagement
- // Either the activity is resizable, or we allow size compat in freeform.
- && (supportsMultiWindow() || mAtmService.mSizeCompatFreeform);
+ return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow();
}
/**
@@ -3357,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--;
@@ -3428,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() {
@@ -3799,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);
@@ -4447,6 +4421,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mVisibleRequested = visible;
+ setInsetsFrozen(!visible);
if (app != null) {
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
}
@@ -5899,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();
}
@@ -7454,8 +7426,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int containingAppWidth = containingAppBounds.width();
final int containingAppHeight = containingAppBounds.height();
- final float containingRatio = Math.max(containingAppWidth, containingAppHeight)
- / (float) Math.min(containingAppWidth, containingAppHeight);
+ final float containingRatio = computeAspectRatio(containingAppBounds);
int activityWidth = containingAppWidth;
int activityHeight = containingAppHeight;
@@ -7519,6 +7490,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * Returns the aspect ratio of the given {@code rect}.
+ */
+ private static float computeAspectRatio(Rect rect) {
+ final int width = rect.width();
+ final int height = rect.height();
+ if (width == 0 || height == 0) {
+ return 0;
+ }
+ return Math.max(width, height) / (float) Math.min(width, height);
+ }
+
+ /**
* @return {@code true} if this activity was reparented to another display but
* {@link #ensureActivityConfiguration} is not called.
*/
@@ -8197,7 +8180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
proto.write(TRANSLUCENT, !occludesParent());
proto.write(VISIBLE, mVisible);
proto.write(VISIBLE_REQUESTED, mVisibleRequested);
- proto.write(CLIENT_VISIBLE, mClientVisible);
+ proto.write(CLIENT_VISIBLE, isClientVisible());
proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
proto.write(REPORTED_DRAWN, mReportedDrawn);
proto.write(REPORTED_VISIBLE, reportedVisible);
@@ -8212,9 +8195,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 af032c108538..0c77d9f1f724 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -58,7 +58,6 @@ import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
@@ -557,7 +556,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
- boolean mSizeCompatFreeform;
boolean mSupportsNonResizableMultiWindow;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -788,8 +786,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
- final boolean sizeCompatFreeform = Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0;
@@ -805,7 +801,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
mForceResizableActivities = forceResizable;
- mSizeCompatFreeform = sizeCompatFreeform;
mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
final boolean multiWindowFormEnabled = freeformWindowManagement
|| supportsSplitScreenMultiWindow
@@ -4047,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;
}
@@ -5555,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 ca67db079dc6..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;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5b685b4a0499..99289e0139b0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1305,7 +1305,7 @@ public class AppTransition implements Dump {
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
- mRemoteAnimationController = new RemoteAnimationController(mService,
+ mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
remoteAnimationAdapter, mHandler);
}
}
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/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 5d6d51377c3f..e6e866d8965d 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -26,6 +32,7 @@ import android.os.SystemClock;
import android.util.proto.ProtoOutputStream;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -35,7 +42,7 @@ import java.util.ArrayList;
class NonAppWindowAnimationAdapter implements AnimationAdapter {
- private final WindowState mWindow;
+ private final WindowContainer mWindow;
private RemoteAnimationTarget mTarget;
private SurfaceControl mCapturedLeash;
@@ -47,22 +54,43 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
return false;
}
- NonAppWindowAnimationAdapter(WindowState w,
- long durationHint, long statusBarTransitionDelay) {
+ NonAppWindowAnimationAdapter(WindowContainer w, long durationHint,
+ long statusBarTransitionDelay) {
mWindow = w;
mDurationHint = durationHint;
mStatusBarTransitionDelay = statusBarTransitionDelay;
}
+ static RemoteAnimationTarget[] startNonAppWindowAnimations(WindowManagerService service,
+ DisplayContent displayContent, @WindowManager.TransitionOldType int transit,
+ long durationHint, long statusBarTransitionDelay) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+ startNonAppWindowAnimationsForKeyguardExit(
+ service, durationHint, statusBarTransitionDelay, targets);
+ } else if (transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT
+ || transit == TRANSIT_OLD_WALLPAPER_CLOSE) {
+ final boolean shouldAttachNavBarToApp =
+ displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ && service.getRecentsAnimationController() == null
+ && displayContent.getFixedRotationAnimationController() == null;
+ if (shouldAttachNavBarToApp) {
+ startNavigationBarWindowAnimation(
+ displayContent, durationHint, statusBarTransitionDelay, targets);
+ }
+ }
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
/**
* Creates and starts remote animations for all the visible non app windows.
*
* @return RemoteAnimationTarget[] targets for all the visible non app windows
*/
- public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit(
- WindowManagerService service, long durationHint, long statusBarTransitionDelay) {
- final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
-
+ private static void startNonAppWindowAnimationsForKeyguardExit(WindowManagerService service,
+ long durationHint, long statusBarTransitionDelay,
+ ArrayList<RemoteAnimationTarget> targets) {
final WindowManagerPolicy policy = service.mPolicy;
service.mRoot.forAllWindows(nonAppWindow -> {
if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
@@ -74,7 +102,22 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
targets.add(nonAppAdapter.createRemoteAnimationTarget());
}
}, true /* traverseTopToBottom */);
- return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Creates and starts remote animation for the navigation bar windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible non app windows
+ */
+ private static void startNavigationBarWindowAnimation(DisplayContent displayContent,
+ long durationHint, long statusBarTransitionDelay,
+ ArrayList<RemoteAnimationTarget> targets) {
+ final WindowState navWindow = displayContent.getDisplayPolicy().getNavigationBar();
+ final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
+ navWindow.mToken, durationHint, statusBarTransitionDelay);
+ navWindow.mToken.startAnimation(navWindow.mToken.getPendingTransaction(),
+ nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
+ targets.add(nonAppAdapter.createRemoteAnimationTarget());
}
/**
@@ -84,7 +127,7 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(),
mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null,
- null);
+ null, mWindow.getWindowType());
return mTarget;
}
@@ -120,8 +163,8 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
@Override
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
- pw.print("token=");
- pw.println(mWindow.mToken);
+ pw.print("window=");
+ pw.println(mWindow);
if (mTarget != null) {
pw.print(prefix);
pw.println("Target:");
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 42cb96f65738..31663b707004 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -60,6 +57,7 @@ class RemoteAnimationController implements DeathRecipient {
private static final long TIMEOUT_MS = 2000;
private final WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
@@ -72,9 +70,10 @@ class RemoteAnimationController implements DeathRecipient {
private boolean mCanceled;
private boolean mLinkedToDeathOfRunner;
- RemoteAnimationController(WindowManagerService service,
+ RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
mService = service;
+ mDisplayContent = displayContent;
mRemoteAnimationAdapter = remoteAnimationAdapter;
mHandler = handler;
}
@@ -221,12 +220,11 @@ class RemoteAnimationController implements DeathRecipient {
private RemoteAnimationTarget[] createNonAppWindowAnimations(
@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()");
- return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
- ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService,
+ return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService,
+ mDisplayContent,
+ transit,
mRemoteAnimationAdapter.getDuration(),
- mRemoteAnimationAdapter.getStatusBarTransitionDelay())
- : new RemoteAnimationTarget[0];
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay());
}
private void onAnimationFinished() {
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 ff65c566e62a..f850e85145df 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
@@ -2841,8 +2832,6 @@ class Task extends WindowContainer<WindowContainer> {
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
&& candidateWindowingMode != WINDOWING_MODE_PINNED
- && (candidateWindowingMode != WINDOWING_MODE_FREEFORM
- || !mTaskSupervisor.mService.mSizeCompatFreeform)
&& !mTaskSupervisor.mService.mSupportsNonResizableMultiWindow) {
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
WINDOWING_MODE_FULLSCREEN);
@@ -3368,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) {
@@ -5063,6 +5043,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");
@@ -7366,6 +7350,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,
@@ -7373,7 +7358,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);
@@ -7408,10 +7393,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);
}
@@ -7504,10 +7503,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 82d9c214ece7..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,26 +595,21 @@ 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,
TaskDisplayArea displayArea) {
- // TODO(176061101): Migrate |mSizeCompatFreeform| to |mSupportsNonResizableMultiWindow|.
- if (!mSupervisor.mService.mSizeCompatFreeform || activity.isResizeable()) {
- return false;
- }
- final DisplayContent display = displayArea.getDisplayContent();
- if (display == null) {
+ if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
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) {
@@ -632,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) {
@@ -663,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(
@@ -672,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
@@ -682,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) {
@@ -711,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
@@ -737,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;
@@ -758,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
@@ -775,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;
}
@@ -860,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;
}
@@ -891,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.
@@ -900,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 ec3c0d033f98..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
@@ -273,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 */);
}
}
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..b3d2afb1cfb2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -29,6 +29,7 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -2684,14 +2685,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 +3061,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;
}
@@ -3307,4 +3305,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mListeners.remove(listener);
unregisterConfigurationChangeListener(listener);
}
+
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return INVALID_WINDOW_TYPE;
+ }
}
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 c0ccd81c9b15..c9e1605f7f0d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,7 +39,6 @@ import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
@@ -224,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;
@@ -757,6 +757,8 @@ public class WindowManagerService extends IWindowManager.Stub
final TaskSnapshotController mTaskSnapshotController;
+ final BlurController mBlurController = new BlurController();
+
boolean mIsTouchDevice;
boolean mIsFakeTouchDevice;
@@ -803,8 +805,6 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
private final Uri mForceResizableUri = Settings.Global.getUriFor(
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
- private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor(
- DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
private final Uri mSupportsNonResizableMultiWindowUri = Settings.Global.getUriFor(
DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW);
private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor(
@@ -831,8 +831,6 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(mSizeCompatFreeformUri, false, this,
- UserHandle.USER_ALL);
resolver.registerContentObserver(mSupportsNonResizableMultiWindowUri, false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this,
@@ -872,11 +870,6 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mSizeCompatFreeformUri.equals(uri)) {
- updateSizeCompatFreeform();
- return;
- }
-
if (mSupportsNonResizableMultiWindowUri.equals(uri)) {
updateSupportsNonResizableMultiWindow();
return;
@@ -974,14 +967,6 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mForceResizableActivities = forceResizable;
}
- void updateSizeCompatFreeform() {
- ContentResolver resolver = mContext.getContentResolver();
- final boolean sizeCompatFreeform = Settings.Global.getInt(resolver,
- DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
-
- mAtmService.mSizeCompatFreeform = sizeCompatFreeform;
- }
-
void updateSupportsNonResizableMultiWindow() {
ContentResolver resolver = mContext.getContentResolver();
final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver,
@@ -3986,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(
@@ -5690,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
// -------------------------------------------------------------
@@ -6429,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) {
@@ -8026,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) {
@@ -8683,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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1fc7041c0fe2..0cc5254c360a 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
@@ -5853,4 +5777,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
-attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
}
+
+ @Override
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return mAttrs.type;
+ }
}
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..5276d9c8a5f1 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,18 @@ 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 */);
+ }
+ }
+
+ @Override
+ @WindowManager.LayoutParams.WindowType int getWindowType() {
+ return windowType;
+ }
}
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/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index bf95f4c51d96..29db7409131e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1326,6 +1326,46 @@ public class QuotaControllerTest {
}
@Test
+ public void testGetMaxJobExecutionTimeLocked_Regular_Active() {
+ JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS);
+ setDischarging();
+ setStandbyBucket(ACTIVE_INDEX, job);
+ setProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+
+ // ACTIVE apps (where allowed time = window size) should be capped at max execution limit.
+ synchronized (mQuotaController.mLock) {
+ assertEquals(2 * HOUR_IN_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ }
+
+ // Make sure sessions are factored in properly.
+ mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS),
+ 30 * MINUTE_IN_MILLIS, 1), false);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(90 * MINUTE_IN_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ }
+
+ mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (5 * HOUR_IN_MILLIS),
+ 30 * MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (4 * HOUR_IN_MILLIS),
+ 30 * MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (3 * HOUR_IN_MILLIS),
+ 25 * MINUTE_IN_MILLIS, 1), false);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+ }
+ }
+
+ @Test
public void testGetMaxJobExecutionTimeLocked_EJ() {
final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
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/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/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 a078a77b4498..a97ea268b1c8 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -21,8 +21,10 @@ import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STA
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertThrows;
+import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -33,10 +35,13 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Optional;
@@ -70,14 +75,14 @@ public final class DeviceStateManagerServiceTest {
public void baseStateChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
}
@@ -89,21 +94,21 @@ public final class DeviceStateManagerServiceTest {
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@@ -116,7 +121,7 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@@ -129,16 +134,19 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void supportedStatesChanged() {
+ public void supportedStatesChanged() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
@@ -146,27 +154,44 @@ public final class DeviceStateManagerServiceTest {
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
+
+ assertArrayEquals(callback.getLastNotifiedInfo().supportedStates,
+ new int[] { DEFAULT_DEVICE_STATE.getIdentifier() });
}
@Test
- public void supportedStatesChanged_unsupportedBaseState() {
+ public void supportedStatesChanged_statesRemainSame() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE });
- // The current requested state is cleared because it is no longer supported.
+ // The current committed and requests states do not change because the current state remains
+ // supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState(), Optional.empty());
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
- mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+ // The callback wasn't notified about a change in supported states as the states have not
+ // changed.
+ assertNull(callback.getLastNotifiedInfo());
+ }
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ @Test
+ public void getDeviceStateInfo() throws RemoteException {
+ DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo();
+ assertNotNull(info);
+ assertArrayEquals(info.supportedStates,
+ new int[] { DEFAULT_DEVICE_STATE.getIdentifier(),
+ OTHER_DEVICE_STATE.getIdentifier() });
+ assertEquals(info.baseState, DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
@@ -175,41 +200,33 @@ public final class DeviceStateManagerServiceTest {
mService.getBinderService().registerCallback(callback);
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
- assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(),
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
OTHER_DEVICE_STATE.getIdentifier());
mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
- assertEquals(callback.getLastNotifiedValue().intValue(),
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
// The callback should not have been notified of the state change as the policy is still
// pending callback.
- assertEquals(callback.getLastNotifiedValue().intValue(),
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
// Now that the policy is finished processing the callback should be notified of the state
// change.
- assertEquals(callback.getLastNotifiedValue().intValue(),
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
OTHER_DEVICE_STATE.getIdentifier());
- }
-
- @Test
- public void registerCallback_emitsInitialValue() throws RemoteException {
- TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
- mService.getBinderService().registerCallback(callback);
- assertNotNull(callback.getLastNotifiedValue());
- assertEquals(callback.getLastNotifiedValue().intValue(),
- DEFAULT_DEVICE_STATE.getIdentifier());
- }
-
- @Test
- public void getSupportedDeviceStates() throws RemoteException {
- final int[] expectedStates = new int[] { 0, 1 };
- assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
}
@Test
@@ -228,11 +245,16 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_ACTIVE);
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
mService.getBinderService().cancelRequest(token);
@@ -240,10 +262,96 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_CANCELED);
// Committed state is set back to the requested state once the override is cleared.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @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
@@ -263,7 +371,7 @@ public final class DeviceStateManagerServiceTest {
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -275,7 +383,7 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_CANCELED);
// Committed state is set back to the requested state once the override is cleared.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE);
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -297,7 +405,7 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_ACTIVE);
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -310,7 +418,7 @@ public final class DeviceStateManagerServiceTest {
// Committed state is set back to the requested state as the override state is no longer
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE);
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -348,6 +456,10 @@ public final class DeviceStateManagerServiceTest {
});
}
+ private static void assertArrayEquals(int[] expected, int[] actual) {
+ Assert.assertTrue(Arrays.equals(expected, actual));
+ }
+
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
private final DeviceStateProvider mProvider;
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -376,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;
}
@@ -429,12 +549,14 @@ public final class DeviceStateManagerServiceTest {
public static final int STATUS_SUSPENDED = 2;
public static final int STATUS_CANCELED = 3;
- private Integer mLastNotifiedValue;
+ @Nullable
+ private DeviceStateInfo mLastNotifiedInfo;
+ private boolean mNotifiedOfChangeInSupportedStates;
private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
@Override
- public void onDeviceStateChanged(int deviceState) {
- mLastNotifiedValue = deviceState;
+ public void onDeviceStateInfoChanged(DeviceStateInfo info) {
+ mLastNotifiedInfo = info;
}
@Override
@@ -453,8 +575,8 @@ public final class DeviceStateManagerServiceTest {
}
@Nullable
- Integer getLastNotifiedValue() {
- return mLastNotifiedValue;
+ DeviceStateInfo getLastNotifiedInfo() {
+ return mLastNotifiedInfo;
}
int getLastNotifiedStatus(IBinder requestToken) {
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..7d9ab3772733 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -20,8 +20,10 @@ import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static com.android.server.job.JobConcurrencyManager.workTypeToString;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -54,7 +56,7 @@ public class WorkCountTrackerTest {
private static final double[] EQUAL_PROBABILITY_CDF =
buildWorkTypeCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
- 1.0 / NUM_WORK_TYPES);
+ 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES);
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -66,8 +68,9 @@ public class WorkCountTrackerTest {
}
@NonNull
- private static double[] buildWorkTypeCdf(double pTop, double pEj, double pBg, double pBgUser) {
- return buildCdf(pTop, pEj, pBg, pBgUser);
+ private static double[] buildWorkTypeCdf(
+ double pTop, double pFgs, double pEj, double pBg, double pBgUser) {
+ return buildCdf(pTop, pFgs, pEj, pBg, pBgUser);
}
@NonNull
@@ -102,10 +105,12 @@ public class WorkCountTrackerTest {
case 0:
return WORK_TYPE_TOP;
case 1:
- return WORK_TYPE_EJ;
+ return WORK_TYPE_FGS;
case 2:
- return WORK_TYPE_BG;
+ return WORK_TYPE_EJ;
case 3:
+ return WORK_TYPE_BG;
+ case 4:
return WORK_TYPE_BGUSER;
default:
throw new IllegalStateException("Unknown work type");
@@ -224,12 +229,15 @@ public class WorkCountTrackerTest {
private void startPendingJobs(Jobs jobs) {
while (hasStartablePendingJob(jobs)) {
- final int startingWorkType =
- getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+ final int workType = getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+ if (jobs.pending.get(workType) > 0) {
+ final int pendingMultiType = getPendingMultiType(jobs, workType);
+ final int startingWorkType = mWorkCountTracker.canJobStart(pendingMultiType);
+ if (startingWorkType == WORK_TYPE_NONE) {
+ continue;
+ }
- if (jobs.pending.get(startingWorkType) > 0
- && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
- final int pendingMultiType = getPendingMultiType(jobs, startingWorkType);
jobs.removePending(pendingMultiType);
jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
mWorkCountTracker.stageJob(startingWorkType, pendingMultiType);
@@ -304,7 +312,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0);
+ final double[] cdf = buildWorkTypeCdf(0.5, 0, 0, 0.5, 0);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -322,7 +330,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.75, .2, .05);
final double probStart = 0.5;
@@ -340,7 +348,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double[] cdf = buildWorkTypeCdf(1.0 / 3, 0, 0, 1.0 / 3, 1.0 / 3);
final double[] numTypesCdf = buildCdf(.05, .95);
final double probStart = 0.5;
@@ -358,7 +366,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.8, .1);
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.8, .1);
final double[] numTypesCdf = buildCdf(.5, .3, .15, .05);
final double probStart = 0.5;
@@ -376,7 +384,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.1, 0);
+ final double[] cdf = buildWorkTypeCdf(0.85, 0.05, 0, 0.1, 0);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -394,7 +402,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.1, 0, 0.1, .8);
+ final double[] cdf = buildWorkTypeCdf(0.1, 0, 0, 0.1, .8);
final double[] numTypesCdf = buildCdf(0.5, 0.5);
final double probStart = 0.5;
@@ -413,7 +421,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.9, 0, 0.05, 0.05);
+ final double[] cdf = buildWorkTypeCdf(0.8, 0.1, 0, 0.05, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -432,7 +440,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.5, 0.5);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.5, 0.5);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -451,7 +459,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.1, 0.9);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.1, 0.9);
final double[] numTypesCdf = buildCdf(0.9, 0.1);
final double probStart = 0.5;
@@ -470,7 +478,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double probStop = 0.5;
- final double[] cdf = buildWorkTypeCdf(0, 0, 0.9, 0.1);
+ final double[] cdf = buildWorkTypeCdf(0, 0, 0, 0.9, 0.1);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -488,7 +496,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.5, 0.5, 0, 0);
+ final double[] cdf = buildWorkTypeCdf(0.5, 0, 0.5, 0, 0);
final double[] numTypesCdf = buildCdf(0.1, 0.7, 0.2);
final double probStart = 0.5;
@@ -511,7 +519,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
final double probStop = 0.13;
- final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.85);
+ final double[] numTypesCdf = buildCdf(0, 0.05, 0.1, 0.8, 0.05);
final double probStart = 0.87;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
@@ -528,7 +536,7 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(.1, 0.5, 0.35, 0.05);
+ final double[] cdf = buildWorkTypeCdf(.1, 0, 0.5, 0.35, 0.05);
final double[] numTypesCdf = buildCdf(1);
final double probStart = 0.5;
@@ -548,7 +556,7 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
- final double[] cdf = buildWorkTypeCdf(0.01, 0.49, 0.1, 0.4);
+ final double[] cdf = buildWorkTypeCdf(0.01, 0.09, 0.4, 0.1, 0.4);
final double[] numTypesCdf = buildCdf(0.7, 0.3);
final double probStart = 0.5;
@@ -576,11 +584,13 @@ public class WorkCountTrackerTest {
startPendingJobs(jobs);
for (Pair<Integer, Integer> run : resultRunning) {
- assertWithMessage("Incorrect running result for work type " + run.first)
+ assertWithMessage(
+ "Incorrect running result for work type " + workTypeToString(run.first))
.that(jobs.running.get(run.first)).isEqualTo(run.second);
}
for (Pair<Integer, Integer> pend : resultPending) {
- assertWithMessage("Incorrect pending result for work type " + pend.first)
+ assertWithMessage(
+ "Incorrect pending result for work type " + workTypeToString(pend.first))
.that(jobs.pending.get(pend.first)).isEqualTo(pend.second);
}
}
@@ -938,10 +948,15 @@ public class WorkCountTrackerTest {
assertThat(jobs.running.get(WORK_TYPE_TOP)).isEqualTo(6);
assertThat(jobs.running.get(WORK_TYPE_EJ)).isEqualTo(1);
assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
- assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(4);
+ // If run the TOP jobs as TOP first, and a TOP|EJ job as EJ, then we'll have 4 TOP jobs
+ // remaining.
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtLeast(4);
+ // If we end up running the TOP|EJ jobs as TOP first, then we'll have 5 TOP jobs remaining.
+ assertThat(jobs.pending.get(WORK_TYPE_TOP)).isAtMost(5);
// Can't equate pending EJ since some could be running as TOP and BG
assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
- assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(9);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtLeast(8);
+ assertThat(jobs.pending.get(WORK_TYPE_BG)).isAtMost(9);
// Stop all jobs
jobs.maybeFinishJobs(1);
@@ -975,7 +990,7 @@ public class WorkCountTrackerTest {
assertThat(jobs.running.get(WORK_TYPE_EJ)).isAtLeast(1);
assertThat(jobs.running.get(WORK_TYPE_BG)).isEqualTo(1);
assertThat(jobs.pending.get(WORK_TYPE_TOP)).isEqualTo(0);
- assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(2);
+ assertThat(jobs.pending.get(WORK_TYPE_EJ)).isAtLeast(1);
assertThat(jobs.pending.get(WORK_TYPE_BG)).isEqualTo(4);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index 2288a8925561..cc18317d0529 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -18,7 +18,9 @@ package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_FGS;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static com.android.server.job.JobConcurrencyManager.workTypeToString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -44,10 +46,12 @@ import java.util.List;
public class WorkTypeConfigTest {
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+ private static final String KEY_MAX_FGS = "concurrency_max_fgs_test";
private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+ private static final String KEY_MIN_FGS = "concurrency_min_fgs_test";
private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@@ -59,15 +63,17 @@ public class WorkTypeConfigTest {
private void resetConfig() {
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_FGS, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_FGS, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, null, false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, null, false);
}
private void check(@Nullable DeviceConfig.Properties config,
@@ -103,10 +109,12 @@ public class WorkTypeConfigTest {
assertEquals(expectedTotal, counts.getMaxTotal());
for (Pair<Integer, Integer> min : expectedMinLimits) {
- assertEquals((int) min.second, counts.getMinReserved(min.first));
+ assertEquals("Incorrect min value for " + workTypeToString(min.first),
+ (int) min.second, counts.getMinReserved(min.first));
}
for (Pair<Integer, Integer> max : expectedMaxLimits) {
- assertEquals((int) max.second, counts.getMax(max.first));
+ assertEquals("Incorrect max value for " + workTypeToString(max.first),
+ (int) max.second, counts.getMax(max.first));
}
}
@@ -193,6 +201,14 @@ public class WorkTypeConfigTest {
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3)),
+ /*expected*/ true, 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 3), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 1), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_FGS, 3)));
check(null, /*default*/ 15,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
@@ -289,5 +305,30 @@ public class WorkTypeConfigTest {
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 16)
+ .setInt(KEY_MAX_TOP, 16)
+ .setInt(KEY_MIN_TOP, 1)
+ .setInt(KEY_MAX_FGS, 15)
+ .setInt(KEY_MIN_FGS, 2)
+ .setInt(KEY_MAX_EJ, 14)
+ .setInt(KEY_MIN_EJ, 3)
+ .setInt(KEY_MAX_BG, 13)
+ .setInt(KEY_MIN_BG, 4)
+ .setInt(KEY_MAX_BGUSER, 12)
+ .setInt(KEY_MIN_BGUSER, 5)
+ .build(),
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_FGS, 2),
+ Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /* max */
+ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_FGS, 15),
+ Pair.create(WORK_TYPE_EJ, 14),
+ Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 12)));
}
}
diff --git a/services/tests/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/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/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/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 990927b704fa..d8e7582633de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1965,17 +1965,17 @@ public class ActivityRecordTests extends WindowTestsBase {
// Non-resizable
mAtm.mForceResizableActivities = false;
- mAtm.mSizeCompatFreeform = false;
+ mAtm.mSupportsNonResizableMultiWindow = false;
assertFalse(activity.supportsFreeform());
// Force resizable
mAtm.mForceResizableActivities = true;
- mAtm.mSizeCompatFreeform = false;
+ mAtm.mSupportsNonResizableMultiWindow = false;
assertTrue(activity.supportsFreeform());
// Allow non-resizable
mAtm.mForceResizableActivities = false;
- mAtm.mSizeCompatFreeform = true;
+ mAtm.mSupportsNonResizableMultiWindow = true;
assertTrue(activity.supportsFreeform());
}
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/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2fdd63ed93d5..ea971809c8f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -18,11 +18,13 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -35,7 +37,9 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
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.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -94,7 +98,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
mAdapter.setCallingPidUid(123, 456);
runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
- mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
+ mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler);
}
private WindowState createAppOverlayWindow() {
@@ -520,6 +524,164 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
}
+ @Test
+ public void testNonAppTarget_sendNavBar() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition();
+
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ boolean containNavTarget = false;
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ containNavTarget = true;
+ break;
+ }
+ }
+ assertTrue(containNavTarget);
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(false).when(policy).shouldAttachNavBarToAppDuringTransition();
+
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ boolean containNavTarget = false;
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ containNavTarget = true;
+ break;
+ }
+ }
+ assertFalse(containNavTarget);
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final FixedRotationAnimationController mockController =
+ mock(FixedRotationAnimationController.class);
+ doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController();
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition();
+
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ boolean containNavTarget = false;
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ containNavTarget = true;
+ break;
+ }
+ }
+ assertFalse(containNavTarget);
+ }
+
+ @Test
+ public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final RecentsAnimationController mockController =
+ mock(RecentsAnimationController.class);
+ doReturn(mockController).when(mWm).getRecentsAnimationController();
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition();
+
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(50, 100), null,
+ new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ boolean containNavTarget = false;
+ for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
+ if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
+ containNavTarget = true;
+ break;
+ }
+ }
+ assertFalse(containNavTarget);
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
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 f8346efba06d..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);
}
@@ -661,7 +730,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
@Test
public void testForceMaximizesUnresizeableApp() {
- mAtm.mSizeCompatFreeform = false;
+ mAtm.mSupportsNonResizableMultiWindow = false;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
@@ -684,7 +753,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
@Test
public void testLaunchesPortraitSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
- mAtm.mSizeCompatFreeform = true;
+ mAtm.mSupportsNonResizableMultiWindow = true;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
@@ -712,7 +781,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
@Test
public void testLaunchesLandscapeSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
- mAtm.mSizeCompatFreeform = true;
+ mAtm.mSupportsNonResizableMultiWindow = true;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
final ActivityOptions options = ActivityOptions.makeBasic();
@@ -728,7 +797,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
@Test
public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
- mAtm.mSizeCompatFreeform = true;
+ mAtm.mSupportsNonResizableMultiWindow = true;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
final ActivityOptions options = ActivityOptions.makeBasic();
@@ -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 4ce8fb39aa8e..79ef8680dfec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -319,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
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/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 0dd8d2305f4c..18c8cf5dfa05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
@@ -119,20 +118,6 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
}
@Test
- public void testEnableSizeCompatFreeform() {
- try (BoolSettingsSession enableSizeCompatFreeformSession = new
- BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
- final boolean enableSizeCompatFreeform =
- !enableSizeCompatFreeformSession.getSetting();
- final Uri enableSizeCompatFreeformUri =
- enableSizeCompatFreeformSession.setSetting(enableSizeCompatFreeform);
- mWm.mSettingsObserver.onChange(false, enableSizeCompatFreeformUri);
-
- assertEquals(mWm.mAtmService.mSizeCompatFreeform, enableSizeCompatFreeform);
- }
- }
-
- @Test
public void testSupportsNonResizableMultiWindow() {
try (BoolSettingsSession supportsNonResizableMultiWindowSession = new
BoolSettingsSession(DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)) {
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..de74f38f1228 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);
}
@@ -1115,7 +1128,7 @@ public class ConnectivityServiceTest {
return mDeviceIdleInternal;
}
},
- mNetworkManagementService, mMockNetd, userId, mKeyStore);
+ mNetworkManagementService, mMockNetd, userId, mVpnProfileStore);
}
public void setUids(Set<UidRange> uids) {
@@ -1294,8 +1307,9 @@ public class ConnectivityServiceTest {
return mVMSHandlerThread;
}
- public KeyStore getKeyStore() {
- return mKeyStore;
+ @Override
+ public VpnProfileStore getVpnProfileStore() {
+ return mVpnProfileStore;
}
public INetd getNetd() {
@@ -1462,8 +1476,6 @@ public class ConnectivityServiceTest {
mDeps = makeDependencies();
returnRealCallingUid();
mService = new ConnectivityService(mServiceContext,
- mNetworkManagementService,
- mStatsService,
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
@@ -1547,6 +1559,7 @@ public class ConnectivityServiceTest {
@After
public void tearDown() throws Exception {
+ unregisterDefaultNetworkCallbacks();
setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
@@ -1652,6 +1665,7 @@ public class ConnectivityServiceTest {
assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
// Test getAllNetworks()
assertEmpty(mCm.getAllNetworks());
+ assertEmpty(mCm.getAllNetworkStateSnapshot());
}
/**
@@ -1680,7 +1694,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 +5492,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 +5518,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 +5540,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 +5592,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 +5609,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 +5621,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 +5648,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 +5667,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 +5683,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 +5936,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 +5976,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 +5995,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();
@@ -7488,8 +7517,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 +7525,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 {
@@ -7826,6 +7854,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 +7894,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 +7933,6 @@ public class ConnectivityServiceTest {
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
- reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
@@ -7912,8 +7948,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 +7989,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 +8032,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 +8051,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 +8064,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 +8153,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 +8187,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 +8226,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 +8268,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 +8961,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
@@ -9467,6 +9501,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();
@@ -9801,6 +9839,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(
@@ -9849,12 +9935,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 +9969,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 +9984,7 @@ public class ConnectivityServiceTest {
// Verify that the active network is correct
verifyActiveNetwork(TRANSPORT_ETHERNET);
+ // default NCs will be unregistered in tearDown
}
@Test
@@ -9905,6 +9992,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 +10013,7 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.getNetwork());
assertFalse(mCm.isActiveNetworkMetered());
+ // default NCs will be unregistered in tearDown
}
@Test
@@ -10081,7 +10170,6 @@ public class ConnectivityServiceTest {
/**
* Test the tracked default requests clear previous OEM requests on setOemNetworkPreference().
- * @throws Exception
*/
@Test
public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception {
@@ -10109,9 +10197,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()
@@ -10177,9 +10264,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()
@@ -10240,10 +10326,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()
@@ -10294,10 +10379,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()
@@ -10347,7 +10431,314 @@ public class ConnectivityServiceTest {
true /* shouldDestroyNetwork */);
}
- private UidRange createUidRange(int userId) {
- return UidRange.createForUser(UserHandle.of(userId));
+ /**
+ * 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