summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp3
-rw-r--r--StubLibraries.bp19
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java19
-rw-r--r--apex/media/framework/java/android/media/ApplicationMediaCapabilities.java84
-rw-r--r--core/api/current.txt21
-rw-r--r--core/api/system-current.txt10
-rw-r--r--core/api/test-current.txt40
-rw-r--r--core/java/android/app/ActivityOptions.java49
-rw-r--r--core/java/android/app/ApplicationPackageManager.java17
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java43
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/java/android/app/backup/FullBackup.java55
-rw-r--r--core/java/android/content/pm/IOnChecksumsReadyListener.aidl27
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/LauncherApps.java1
-rw-r--r--core/java/android/content/pm/PackageManager.java25
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java19
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java74
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java191
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateRequest.java163
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManager.aidl40
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl37
-rw-r--r--core/java/android/hardware/face/FaceAuthenticationFrame.java75
-rw-r--r--core/java/android/hardware/face/FaceDataFrame.java151
-rw-r--r--core/java/android/hardware/face/FaceEnrollCell.java96
-rw-r--r--core/java/android/hardware/face/FaceEnrollFrame.java103
-rw-r--r--core/java/android/hardware/face/FaceEnrollStage.java68
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java2
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl1
-rw-r--r--core/java/android/net/NetworkPolicyManager.java25
-rw-r--r--core/java/android/net/OemNetworkPreferences.java71
-rw-r--r--core/java/android/os/UserManager.java11
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java13
-rw-r--r--core/java/android/service/resumeonreboot/ResumeOnRebootService.java14
-rw-r--r--core/java/android/service/storage/ExternalStorageService.java4
-rw-r--r--core/java/android/service/voice/VoiceInteractionServiceInfo.java9
-rw-r--r--core/java/android/view/Display.java80
-rw-r--r--core/java/android/view/DisplayInfo.java19
-rw-r--r--core/java/android/view/IRemoteAnimationRunner.aidl6
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java3
-rw-r--r--core/java/android/view/InsetsState.java4
-rw-r--r--core/java/android/view/SurfaceControl.java11
-rw-r--r--core/java/android/view/ViewRootImpl.java2
-rw-r--r--core/java/android/view/WindowManagerGlobal.java8
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java7
-rw-r--r--core/java/android/widget/CheckBox.java2
-rw-r--r--core/java/android/widget/CompoundButton.java28
-rw-r--r--core/java/android/widget/RadioButton.java2
-rw-r--r--core/java/android/widget/RadioGroup.java2
-rw-r--r--core/java/android/widget/RemoteViews.java114
-rw-r--r--core/java/android/widget/Switch.java64
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/com/android/internal/widget/DisableImageView.java68
-rw-r--r--core/java/com/android/internal/widget/OWNERS1
-rw-r--r--core/java/com/android/server/BootReceiver.java60
-rw-r--r--core/jni/Android.bp6
-rw-r--r--core/jni/android_view_SurfaceControl.cpp12
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/layout/work_widget_mask_view.xml3
-rw-r--r--core/res/res/values/attrs.xml3
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java155
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTests.java527
-rw-r--r--data/etc/services.core.protolog.json18
-rw-r--r--graphics/java/android/graphics/Typeface.java6
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java31
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java15
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java15
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt6
-rw-r--r--libs/hwui/jni/fonts/FontFamily.cpp26
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java12
-rw-r--r--media/jni/Android.bp4
-rw-r--r--media/jni/android_media_tv_Tuner.cpp2
-rw-r--r--media/jni/tuner/DemuxClient.cpp43
-rw-r--r--media/jni/tuner/DemuxClient.h5
-rw-r--r--media/jni/tuner/FilterClient.cpp118
-rw-r--r--media/jni/tuner/FilterClient.h15
-rw-r--r--media/jni/tuner/FrontendClient.cpp452
-rw-r--r--media/jni/tuner/FrontendClient.h4
-rw-r--r--media/jni/tuner/TimeFilterClient.cpp47
-rw-r--r--media/jni/tuner/TimeFilterClient.h11
-rw-r--r--media/jni/tuner/TunerClient.cpp29
-rw-r--r--media/jni/tuner/TunerClient.h2
-rw-r--r--native/android/system_fonts.cpp2
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityManager.java36
-rw-r--r--packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl6
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java58
-rw-r--r--packages/SystemUI/res/drawable/controls_dialog_bg.xml21
-rw-r--r--packages/SystemUI/res/drawable/udfps_progress_bar.xml44
-rw-r--r--packages/SystemUI/res/layout/controls_in_dialog.xml45
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml11
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml22
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml15
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml3
-rw-r--r--packages/SystemUI/res/values/attrs.xml8
-rw-r--r--packages/SystemUI/res/values/colors.xml4
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/styles.xml21
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java231
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java421
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java44
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java112
-rw-r--r--services/core/Android.bp18
-rw-r--r--services/core/java/android/content/pm/OWNERS1
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java25
-rw-r--r--services/core/java/android/os/OWNERS1
-rw-r--r--services/core/java/com/android/server/BundleUtils.java49
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java123
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java7
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java46
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java103
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java31
-rw-r--r--services/core/java/com/android/server/connectivity/DnsManager.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java4
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java112
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java34
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceState.java10
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java501
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java85
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java4
-rw-r--r--services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java8
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java56
-rw-r--r--services/core/java/com/android/server/location/eventlog/LocalEventLog.java (renamed from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java)4
-rw-r--r--services/core/java/com/android/server/location/injector/LocationEventLog.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java24
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java17
-rw-r--r--services/core/java/com/android/server/os/NativeTombstoneManager.java241
-rw-r--r--services/core/java/com/android/server/os/NativeTombstoneManagerService.java50
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java39
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java118
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java299
-rw-r--r--services/core/java/com/android/server/pm/IncrementalStates.java18
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java37
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java84
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java8
-rw-r--r--services/core/java/com/android/server/pm/ProcessLoggingHandler.java32
-rw-r--r--services/core/java/com/android/server/pm/RestrictionsSet.java3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java100
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java22
-rw-r--r--services/core/java/com/android/server/pm/UserTypeDetails.java85
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java27
-rw-r--r--services/core/java/com/android/server/policy/DisplayFoldController.java4
-rw-r--r--services/core/java/com/android/server/policy/IconUtilities.java151
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java2
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java3
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java85
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java33
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java38
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java7
-rw-r--r--services/core/java/com/android/server/wm/AlertWindowNotification.java9
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java17
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java18
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java2
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java12
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java8
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java42
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java156
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java14
-rw-r--r--services/core/jni/Android.bp8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java149
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java40
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java37
-rw-r--r--services/incremental/Android.bp6
-rw-r--r--services/java/com/android/server/SystemServer.java54
-rw-r--r--services/tests/servicestests/Android.bp6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java273
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java17
-rw-r--r--services/tests/uiservicestests/Android.bp2
-rw-r--r--services/tests/wmtests/Android.bp2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java209
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java5
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java15
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java106
-rw-r--r--tests/net/common/java/android/net/OemNetworkPreferencesTest.java92
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java47
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java68
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java2
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java7
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java32
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java7
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java10
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java90
244 files changed, 7604 insertions, 3139 deletions
diff --git a/Android.bp b/Android.bp
index 202e548d56c8..9655daf649c7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -536,7 +536,7 @@ java_library {
"android.hardware.vibrator-V1.3-java",
"android.security.apc-java",
"android.security.authorization-java",
- "android.system.keystore2-java",
+ "android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
"devicepolicyprotosnano",
@@ -760,6 +760,7 @@ gensrcs {
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
":service-permission-protos",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f16f572..86364af20812 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@ stubs_defaults {
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"stable.core.platform.api.stubs",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- "sdk_system_30_android",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
],
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
@@ -404,7 +405,11 @@ java_library_static {
"android_defaults_stubs_current",
"android_stubs_dists_default",
],
- libs: ["sdk_system_29_android"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
static_libs: ["art.module.public.api.stubs"],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8a281717754..d249f2ae813c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -332,7 +331,7 @@ public final class ConnectivityController extends RestrictingController implemen
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -350,7 +349,7 @@ public final class ConnectivityController extends RestrictingController implemen
if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -380,18 +379,16 @@ public final class ConnectivityController extends RestrictingController implemen
private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- final NetworkCapabilities required;
// A restricted job that's out of quota MUST use an unmetered network.
if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
&& !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
- required = new NetworkCapabilities(
+ final NetworkCapabilities required = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ .addCapability(NET_CAPABILITY_NOT_METERED).build();
+ return required.satisfiedByNetworkCapabilities(capabilities);
} else {
- required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+ return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
}
-
- return required.satisfiedByNetworkCapabilities(capabilities);
}
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -402,9 +399,9 @@ public final class ConnectivityController extends RestrictingController implemen
}
// See if we match after relaxing any unmetered request
- final NetworkCapabilities relaxed = new NetworkCapabilities(
+ final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .removeCapability(NET_CAPABILITY_NOT_METERED);
+ .removeCapability(NET_CAPABILITY_NOT_METERED).build();
if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
// TODO: treat this as "maybe" response; need to check quotas
return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index e1a859648ee6..aefeab621778 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -36,30 +36,46 @@ import java.util.Map;
import java.util.Set;
/**
- * ApplicationMediaCapabilities is an immutable class that encapsulates an application's
- * capabilities for handling newer video codec format and media features.
- *
- * The ApplicationMediaCapabilities class is used by the platform to represent an application's
- * media capabilities as defined in their manifest(TODO: Add link) in order to determine
- * whether modern media files need to be transcoded for that application (TODO: Add link).
- *
- * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- * control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- * provided by applications at runtime like this override the default manifest capabilities for that
- * media access.
- *
- * <h3> Video Codec Support</h3>
- * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- * for newer format with this class as they are assumed to support older format like h.264.
- *
- * <h4>Capability of handling HDR(high dynamic range) video</h4>
- * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- * application will only need to specify individual types they supported.
+ ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
+ 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:
+ <pre>
+ {@code
+ <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="HDR10Plus" supported="false"/>
+ </media-capabilities>
+ }
+ </pre>
+ The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
+ represent an application's media capabilities in order to determine whether modern media files need
+ to be transcoded for that application.
+ </p>
+
+ <p>
+ ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
+ {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
+ control over the transcoding that is built into the platform. ApplicationMediaCapabilities
+ provided by applications at runtime like this override the default manifest capabilities for that
+ media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
+ through the builder class {@link ApplicationMediaCapabilities.Builder}
+
+ <h3> Video Codec Support</h3>
+ <p>
+ Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
+ for newer format with this class as they are assumed to support older format like h.264.
+
+ <h3>Capability of handling HDR(high dynamic range) video</h3>
+ <p>
+ There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
+ application will only need to specify individual types they supported.
*/
-// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
-// TODO(hkuang): Add a link to seamless transcoding detail when it is published
-// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
@@ -105,9 +121,9 @@ public final class ApplicationMediaCapabilities implements Parcelable {
*/
public boolean isVideoMimeTypeSupported(
@NonNull String videoMime) throws FormatNotFoundException {
- if (mUnsupportedVideoMimeTypes.contains(videoMime)) {
+ if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return false;
- } else if (mSupportedVideoMimeTypes.contains(videoMime)) {
+ } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return true;
} else {
throw new FormatNotFoundException(videoMime);
@@ -262,11 +278,27 @@ public final class ApplicationMediaCapabilities implements Parcelable {
/**
* Creates {@link ApplicationMediaCapabilities} from an xml.
+ *
+ * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
+ * <p> Here is an example:
+ *
+ * <pre>
+ * {@code
+ * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ * <format android:name="HEVC" supported="true"/>
+ * <format android:name="HDR10" supported="false"/>
+ * <format android:name="HDR10Plus" supported="false"/>
+ * </media-capabilities>
+ * }
+ * </pre>
+ * <p>
+ *
* @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
* @return An ApplicationMediaCapabilities object.
* @throws UnsupportedOperationException if the capabilities in xml config are invalid or
* incompatible.
*/
+ // TODO: Add developer.android.com link for the format of the xml.
@NonNull
public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
@@ -430,7 +462,7 @@ public final class ApplicationMediaCapabilities implements Parcelable {
mIsSlowMotionSupported = isSupported;
break;
default:
- throw new UnsupportedOperationException("Invalid format name " + name);
+ Log.w(TAG, "Invalid format name " + name);
}
// Save the name and isSupported into the map for validate later.
mFormatSupportedMap.put(name, isSupported);
diff --git a/core/api/current.txt b/core/api/current.txt
index e511de780ac8..bfdfb4060078 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12298,7 +12298,7 @@ package android.content.pm {
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
- method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+ method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12318,6 @@ package android.content.pm {
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
- field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12503,10 @@ package android.content.pm {
ctor public PackageManager.NameNotFoundException(String);
}
+ @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+ method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+ }
+
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();
@@ -31445,6 +31448,7 @@ package android.os {
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
+ method public static boolean isHeadlessSystemUserMode();
method public boolean isManagedProfile();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
@@ -53345,7 +53349,7 @@ package android.widget {
method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int);
}
- public class CheckBox extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton {
ctor public CheckBox(android.content.Context);
ctor public CheckBox(android.content.Context, android.util.AttributeSet);
ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
@@ -53411,6 +53415,7 @@ package android.widget {
method public boolean isChecked();
method public void setButtonDrawable(@DrawableRes int);
method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable);
+ method public void setButtonIcon(@Nullable android.graphics.drawable.Icon);
method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setButtonTintList(@Nullable android.content.res.ColorStateList);
method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode);
@@ -54451,14 +54456,14 @@ package android.widget {
field protected String[] mExcludeMimes;
}
- public class RadioButton extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton {
ctor public RadioButton(android.content.Context);
ctor public RadioButton(android.content.Context, android.util.AttributeSet);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int);
}
- public class RadioGroup extends android.widget.LinearLayout {
+ @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout {
ctor public RadioGroup(android.content.Context);
ctor public RadioGroup(android.content.Context, android.util.AttributeSet);
method public void check(@IdRes int);
@@ -54580,6 +54585,7 @@ package android.widget {
method public void setChronometerCountDown(@IdRes int, boolean);
method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setCompoundButtonChecked(@IdRes int, boolean);
method public void setContentDescription(@IdRes int, CharSequence);
method public void setDisplayedChild(@IdRes int, int);
method public void setDouble(@IdRes int, String, double);
@@ -54604,6 +54610,7 @@ package android.widget {
method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
method public void setProgressBar(@IdRes int, int, int, boolean);
+ method public void setRadioGroupChecked(@IdRes int, @IdRes int);
method public void setRelativeScrollPosition(@IdRes int, int);
method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
method public void setRemoteAdapter(@IdRes int, android.content.Intent);
@@ -54970,7 +54977,7 @@ package android.widget {
ctor public StackView(android.content.Context, android.util.AttributeSet, int, int);
}
- public class Switch extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton {
ctor public Switch(android.content.Context);
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
@@ -55001,12 +55008,14 @@ package android.widget {
method public void setTextOff(CharSequence);
method public void setTextOn(CharSequence);
method public void setThumbDrawable(android.graphics.drawable.Drawable);
+ method public void setThumbIcon(@Nullable android.graphics.drawable.Icon);
method public void setThumbResource(@DrawableRes int);
method public void setThumbTextPadding(int);
method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setThumbTintList(@Nullable android.content.res.ColorStateList);
method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
+ method public void setTrackIcon(@Nullable android.graphics.drawable.Icon);
method public void setTrackResource(@DrawableRes int);
method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setTrackTintList(@Nullable android.content.res.ColorStateList);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1977babe0bfb..c91b50b55c51 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -290,6 +290,7 @@ package android {
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int hotwordDetectionService = 16844326; // 0x1010626
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -9509,6 +9510,11 @@ package android.security.keystore {
method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
+ public abstract class KeyProperties {
+ field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+ field public static final int NAMESPACE_WIFI = 102; // 0x66
+ }
+
}
package android.security.keystore.recovery {
@@ -10331,7 +10337,7 @@ package android.service.storage {
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
- method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+ method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11704,6 +11710,7 @@ package android.telephony {
method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11711,6 +11718,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e88b6b92581e..7674bfd8827d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -12,6 +12,7 @@ package android {
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
+ field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
@@ -28,6 +29,7 @@ package android {
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+ field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -119,6 +121,7 @@ package android.app {
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchTaskId(int);
@@ -390,11 +393,10 @@ package android.app.admin {
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
- field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
- field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -899,6 +901,40 @@ 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 @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);
+ }
+
+ public static interface DeviceStateManager.DeviceStateListener {
+ method public void onDeviceStateChanged(int);
+ }
+
+ public final class DeviceStateRequest {
+ method public int getFlags();
+ method public int getState();
+ method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int);
+ field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1
+ }
+
+ public static final class DeviceStateRequest.Builder {
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest build();
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int);
+ }
+
+ public static interface DeviceStateRequest.Callback {
+ method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ }
+
+}
+
package android.hardware.display {
public class AmbientDisplayConfiguration {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2b5e18d3feec..28da1c3a3eb7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
package android.app;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -310,6 +311,9 @@ public class ActivityOptions {
private static final String KEY_REMOTE_TRANSITION =
"android:activity.remoteTransition";
+ private static final String KEY_OVERRIDE_TASK_TRANSITION =
+ "android:activity.overrideTaskTransition";
+
/**
* @see #setLaunchCookie
* @hide
@@ -393,6 +397,7 @@ public class ActivityOptions {
private RemoteAnimationAdapter mRemoteAnimationAdapter;
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
+ private boolean mOverrideTaskTransition;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -476,6 +481,40 @@ public class ActivityOptions {
}
/**
+ * Create an ActivityOptions specifying a custom animation to run when the activity in the
+ * different task is displayed.
+ *
+ * @param context Who is defining this. This is the application that the
+ * animation resources will be loaded from.
+ * @param enterResId A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitResId A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param handler If <var>listener</var> is non-null this must be a valid
+ * Handler on which to dispatch the callback; otherwise it should be null.
+ * @param startedListener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @param finishedListener Optional OnAnimationFinishedListener when the animation
+ * has finished running.
+ *
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ * @hide
+ */
+ @RequiresPermission(START_TASKS_FROM_RECENTS)
+ @TestApi
+ public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
+ int enterResId, int exitResId, @Nullable Handler handler,
+ @Nullable OnAnimationStartedListener startedListener,
+ @Nullable OnAnimationFinishedListener finishedListener) {
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+ startedListener, finishedListener);
+ opts.mOverrideTaskTransition = true;
+ return opts;
+ }
+
+ /**
* Creates an ActivityOptions specifying a custom animation to run in place on an existing
* activity.
*
@@ -1107,6 +1146,7 @@ public class ActivityOptions {
mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
+ mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
}
/**
@@ -1561,6 +1601,12 @@ public class ActivityOptions {
return mLaunchCookie;
}
+
+ /** @hide */
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
/**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
@@ -1789,6 +1835,9 @@ public class ActivityOptions {
if (mRemoteTransition != null) {
b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder());
}
+ if (mOverrideTaskTransition) {
+ b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
+ }
return b;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac8c988..8ac91396a6b0 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
try {
if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@ public class ApplicationPackageManager extends PackageManager {
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ "list of certificates.");
}
+ IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+ new IOnChecksumsReadyListener.Stub() {
+ @Override
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ onChecksumsReadyListener.onChecksumsReady(checksums);
+ }
+ };
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
- encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+ encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+ getUserId());
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
throw new RuntimeException(e);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 642bce4eea08..06fe9d764f25 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1730,8 +1730,12 @@ public class DevicePolicyManager {
* Broadcast action to notify ManagedProvisioning that
* {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
* @hide
+ * @deprecated No longer needed as ManagedProvisioning no longer handles
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
*/
+ // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
"android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
@@ -5469,26 +5473,6 @@ public class DevicePolicyManager {
"android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
/**
- * Broadcast action: notify managed provisioning that the device has been provisioned.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
- "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
- /**
- * Broadcast action: notify managed provisioning that a new managed profile is created.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_PROFILE_CREATED =
- "android.app.action.MANAGED_PROFILE_CREATED";
-
- /**
* Widgets are enabled in keyguard
*/
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -13268,4 +13252,23 @@ public class DevicePolicyManager {
}
}
}
+
+ /**
+ * Resets the default cross profile intent filters that were set during
+ * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+ * profiles if any.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ mService.resetDefaultCrossProfileIntentFilters(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f9ee15393e93..cf0b31ea8cb2 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -498,4 +498,6 @@ interface IDevicePolicyManager {
UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+ void resetDefaultCrossProfileIntentFilters(int userId);
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883edaf2..742d05c1ffa4 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -460,24 +461,40 @@ public class FullBackup {
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes)
throws IOException, XmlPullParserException {
+ verifyTopLevelTag(parser, "full-backup-content");
+
+ parseRules(parser, excludes, includes, Optional.empty());
+
+ logParsingResults(excludes, includes);
+ }
+
+ private void verifyTopLevelTag(XmlPullParser parser, String tag)
+ throws XmlPullParserException, IOException {
int event = parser.getEventType(); // START_DOCUMENT
while (event != XmlPullParser.START_TAG) {
event = parser.next();
}
- if (!"full-backup-content".equals(parser.getName())) {
+ if (!tag.equals(parser.getName())) {
throw new XmlPullParserException("Xml file didn't start with correct tag" +
- " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ " (" + tag + " ). Found \"" + parser.getName() + "\"");
}
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "====================================================");
- Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
Log.v(TAG_XML_PARSER, "====================================================");
Log.v(TAG_XML_PARSER, "");
}
+ }
+ private void parseRules(XmlPullParser parser,
+ Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes,
+ Optional<Integer> maybeRequiredFlags)
+ throws IOException, XmlPullParserException {
+ int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@ public class FullBackup {
break;
}
- int requiredFlags = 0; // no transport flags are required by default
- if (TAG_INCLUDE.equals(parser.getName())) {
- // requiredFlags are only supported for <include /> tag, for <exclude />
- // we should always leave them as the default = 0
- requiredFlags = getRequiredFlagsFromString(
- parser.getAttributeValue(null, "requireFlags"));
- }
+ int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
// retrieve the include/exclude set we'll be adding this rule to
Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@ public class FullBackup {
// Special case for sharedpref files (not dirs) also add ".xml" suffix file.
if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
- !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+ !canonicalFile.getCanonicalPath().endsWith(".xml")) {
final String canonicalXmlPath =
canonicalFile.getCanonicalPath() + ".xml";
activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@ public class FullBackup {
}
}
}
+ }
+
+ private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes) {
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@ public class FullBackup {
return flags;
}
+ private int getRequiredFlagsForRule(XmlPullParser parser,
+ Optional<Integer> maybeRequiredFlags) {
+ if (maybeRequiredFlags.isPresent()) {
+ // This is the new config format where required flags are specified for the whole
+ // section, not per rule.
+ return maybeRequiredFlags.get();
+ }
+
+ if (TAG_INCLUDE.equals(parser.getName())) {
+ // In the legacy config, requiredFlags are only supported for <include /> tag,
+ // for <exclude /> we should always leave them as the default = 0.
+ return getRequiredFlagsFromString(
+ parser.getAttributeValue(null, "requireFlags"));
+ }
+
+ return 0;
+ }
+
private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/content/pm/IOnChecksumsReadyListener.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
new file mode 100644
index 000000000000..7963ce19956a
--- /dev/null
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+ void onChecksumsReady(in List<ApkChecksum> checksums);
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b8829bbf1ca5..a46876ec53c4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@ import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@ interface IPackageManager {
void notifyPackagesReplacedReceived(in String[] packages);
- void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+ void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
//------------------------------------------------------------------------
//
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 62925757cde1..0c0e4020d04a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -450,6 +450,7 @@ public class LauncherApps {
FLAG_MATCH_PINNED,
FLAG_MATCH_MANIFEST,
FLAG_MATCH_CACHED,
+ FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
FLAG_GET_KEY_FIELDS_ONLY,
FLAG_GET_PERSONS_DATA,
})
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d09d83f0cd1d..b95b991b095c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3858,13 +3858,6 @@ public abstract class PackageManager {
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
- /**
- * Extra field name for the ID of a package pending verification. Passed to
- * a package verifier and is used to call back to
- * @see #requestChecksums
- */
- public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
/**
* Permission flag: The permission is set in its current state
* by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@ public abstract class PackageManager {
*/
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
+ /** Listener that gets notified when checksums are available. */
+ @FunctionalInterface
+ public interface OnChecksumsReadyListener {
+ /**
+ * Called when the checksums are available.
+ *
+ * @param checksums array of checksums.
+ */
+ void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+ }
+
/**
* Requesting the checksums for APKs within a package.
- * The checksums will be returned asynchronously via statusReceiver.
+ * The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
* - enforced by platform,
@@ -8730,15 +8734,14 @@ public abstract class PackageManager {
* {@link #TRUST_ALL} will return checksums from any installer,
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
* otherwise the list has to be non-empty list of certificates.
- * @param statusReceiver called once when the results are available as
- * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+ * @param onChecksumsReadyListener called once when the results are available.
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index cbd2c550a797..9012b5ce2b1e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -108,17 +108,14 @@ public class ParsedPermissionUtils {
permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
- if (permission.getProtectionFlags() != 0) {
- if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
- == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_SIGNATURE
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_INTERNAL) {
- return input.error("<permission> protectionLevel specifies a non-instant flag "
- + "but is not based on signature or internal type");
- }
+ final int otherProtectionFlags = permission.getProtectionFlags()
+ & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+ | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+ if (otherProtectionFlags != 0
+ && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
+ && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+ return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+ + " non-runtimeOnly flag but is not based on signature or internal type");
}
return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 29a6ee278d9c..f175e7b00b7e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,8 +16,12 @@
package android.hardware.devicestate;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import java.util.concurrent.Executor;
@@ -28,13 +32,19 @@ import java.util.concurrent.Executor;
*
* @hide
*/
+@TestApi
@SystemService(Context.DEVICE_STATE_SERVICE)
public final class DeviceStateManager {
- /** Invalid device state. */
+ /**
+ * Invalid device state.
+ *
+ * @hide
+ */
public static final int INVALID_DEVICE_STATE = -1;
- private DeviceStateManagerGlobal mGlobal;
+ private final DeviceStateManagerGlobal mGlobal;
+ /** @hide */
public DeviceStateManager() {
DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
if (global == null) {
@@ -45,23 +55,73 @@ public final class DeviceStateManager {
}
/**
+ * Returns the list of device states that are supported and can be requested with
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ @NonNull
+ public int[] getSupportedStates() {
+ return mGlobal.getSupportedStates();
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestState(request, callback, executor);
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if the {@code request} has not been submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ mGlobal.cancelRequest(request);
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @param listener the listener to register.
* @param executor the executor to process notifications.
+ * @param listener the listener to register.
*
* @see DeviceStateListener
*/
- public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
- @NonNull Executor executor) {
+ public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DeviceStateListener listener) {
mGlobal.registerDeviceStateListener(listener, executor);
}
/**
* Unregisters a listener previously registered with
- * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+ * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
*/
- public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
+ public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
mGlobal.unregisterDeviceStateListener(listener);
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c8905038d056..b9ae88ea840f 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -20,9 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +69,9 @@ public final class DeviceStateManagerGlobal {
@GuardedBy("mLock")
private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
+
@Nullable
@GuardedBy("mLock")
private Integer mLastReceivedState;
@@ -77,9 +82,84 @@ public final class DeviceStateManagerGlobal {
}
/**
+ * Returns the set of supported device states.
+ *
+ * @see DeviceStateManager#getSupportedStates()
+ */
+ public int[] getSupportedStates() {
+ try {
+ return mDeviceStateManager.getSupportedDeviceStates();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ *
+ * @see DeviceStateManager#requestState(DeviceStateRequest,
+ * Executor, DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+
+ try {
+ mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+ *
+ * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
+ */
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ final IBinder token = findRequestTokenLocked(request);
+ if (token == null) {
+ // This request has not been submitted.
+ return;
+ }
+
+ try {
+ mDeviceStateManager.cancelRequest(token);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@@ -112,7 +192,7 @@ public final class DeviceStateManagerGlobal {
* Unregisters a listener previously registered with
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void unregisterDeviceStateListener(DeviceStateListener listener) {
@@ -144,6 +224,17 @@ public final class DeviceStateManagerGlobal {
return -1;
}
+ @Nullable
+ private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.valueAt(i).mRequest.equals(request)) {
+ return mRequests.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /** Handles a call from the server that the device state has changed. */
private void handleDeviceStateChanged(int newDeviceState) {
ArrayList<DeviceStateListenerWrapper> listeners;
synchronized (mLock) {
@@ -156,11 +247,68 @@ public final class DeviceStateManagerGlobal {
}
}
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * active.
+ */
+ private void handleRequestActive(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestActive();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * suspended.
+ */
+ private void handleRequestSuspended(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestSuspended();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * canceled.
+ */
+ private void handleRequestCanceled(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.remove(token);
+ }
+ if (request != null) {
+ request.notifyRequestCanceled();
+ }
+ }
+
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateChanged(int deviceState) {
handleDeviceStateChanged(deviceState);
}
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ handleRequestActive(token);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ handleRequestSuspended(token);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ handleRequestCanceled(token);
+ }
}
private static final class DeviceStateListenerWrapper {
@@ -176,4 +324,43 @@ public final class DeviceStateManagerGlobal {
mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
}
}
+
+ private static final class DeviceStateRequestWrapper {
+ private final DeviceStateRequest mRequest;
+ @Nullable
+ private final DeviceStateRequest.Callback mCallback;
+ @Nullable
+ private final Executor mExecutor;
+
+ DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ mRequest = request;
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ void notifyRequestActive() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
+ }
+
+ void notifyRequestSuspended() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+
+ void notifyRequestCanceled() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
new file mode 100644
index 000000000000..70f7002597ed
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.TestApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A request to alter the state of the device managed by {@link DeviceStateManager}.
+ * <p>
+ * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to
+ * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
+ * occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the request. For example, the
+ * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the
+ * request whenever the base (non-override) device state changes.
+ *
+ * @see DeviceStateManager
+ *
+ * @hide
+ */
+@TestApi
+public final class DeviceStateRequest {
+ /**
+ * Flag that indicates the request should be canceled automatically when the base
+ * (non-override) device state changes. Useful when the requestor only wants the request to
+ * remain active while the base state remains constant and automatically cancel when the user
+ * manipulates the device into a different state.
+ */
+ public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0;
+
+ /** @hide */
+ @IntDef(prefix = {"FLAG_"}, flag = true, value = {
+ FLAG_CANCEL_WHEN_BASE_CHANGES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestFlags {}
+
+ /**
+ * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported
+ * states for the device which can be queried with a call to
+ * {@link DeviceStateManager#getSupportedStates()}.
+ *
+ * @param requestedState the device state being requested.
+ */
+ @NonNull
+ public static Builder newBuilder(int requestedState) {
+ return new Builder(requestedState);
+ }
+
+ /**
+ * Builder for {@link DeviceStateRequest}. An instance can be obtained through
+ * {@link #newBuilder(int)}.
+ */
+ public static final class Builder {
+ private final int mRequestedState;
+ private int mFlags;
+
+ private Builder(int requestedState) {
+ mRequestedState = requestedState;
+ }
+
+ /**
+ * Sets the flag bits provided within {@code flags} with all other bits remaining
+ * unchanged.
+ */
+ @NonNull
+ public Builder setFlags(@RequestFlags int flags) {
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the
+ * builder.
+ */
+ @NonNull
+ public DeviceStateRequest build() {
+ return new DeviceStateRequest(mRequestedState, mFlags);
+ }
+ }
+
+ /** Callback to track the status of a request. */
+ public interface Callback {
+ /**
+ * Called to indicate the request has become active and the device state will match the
+ * requested state.
+ * <p>
+ * Guaranteed to be called after a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+ * matching the requested state.
+ */
+ default void onRequestActivated(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been temporarily suspended.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ */
+ default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been canceled. The request can be resubmitted with
+ * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ * <p>
+ * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
+ * occur before this method.
+ */
+ default void onRequestCanceled(@NonNull DeviceStateRequest request) {}
+ }
+
+ private final int mRequestedState;
+ @RequestFlags
+ private final int mFlags;
+
+ private DeviceStateRequest(int requestedState, @RequestFlags int flags) {
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public int getState() {
+ return mRequestedState;
+ }
+
+ @RequestFlags
+ public int getFlags() {
+ return mFlags;
+ }
+}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index a157b3311ca5..323ad21e4884 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -20,5 +20,45 @@ import android.hardware.devicestate.IDeviceStateManagerCallback;
/** @hide */
interface IDeviceStateManager {
+ /**
+ * 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.
+ *
+ * @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
+ * call to this method.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ void requestState(IBinder token, int state, int flags);
+
+ /**
+ * Cancels a request previously submitted with a call to
+ * {@link #requestState(IBinder, int, int)}.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if the supplied {@code token} has not been previously
+ * requested.
+ */
+ void cancelRequest(IBinder token);
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index d1c581361b62..ee2a071741ef 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -18,5 +18,42 @@ package android.hardware.devicestate;
/** @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.
+ *
+ * @param deviceState the new state of the device.
+ */
oneway void onDeviceStateChanged(int deviceState);
+
+ /**
+ * 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.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestActive(IBinder token);
+
+ /**
+ * 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.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestSuspended(IBinder token);
+
+ /**
+ * 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.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestCanceled(IBinder token);
}
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
new file mode 100644
index 000000000000..f39d63411825
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -0,0 +1,75 @@
+/*
+ * 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.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face authentication.
+ *
+ * @hide
+ */
+public final class FaceAuthenticationFrame implements Parcelable {
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face authentication.
+ *
+ * @param data Information about the current frame.
+ */
+ public FaceAuthenticationFrame(@NonNull FaceDataFrame data) {
+ mData = data;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceAuthenticationFrame(@NonNull Parcel source) {
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceAuthenticationFrame> CREATOR =
+ new Creator<FaceAuthenticationFrame>() {
+
+ @Override
+ public FaceAuthenticationFrame createFromParcel(Parcel source) {
+ return new FaceAuthenticationFrame(source);
+ }
+
+ @Override
+ public FaceAuthenticationFrame[] newArray(int size) {
+ return new FaceAuthenticationFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
new file mode 100644
index 000000000000..3a0e09b70b50
--- /dev/null
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -0,0 +1,151 @@
+/*
+ * 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.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @hide
+ */
+public final class FaceDataFrame implements Parcelable {
+ private final int mAcquiredInfo;
+ private final int mVendorCode;
+ private final float mPan;
+ private final float mTilt;
+ private final float mDistance;
+ private final boolean mIsCancellable;
+
+ /**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @param acquiredInfo An integer corresponding to a known acquired message.
+ * @param vendorCode An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@code FACE_ACQUIRED_VENDOR}.
+ * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param distance The distance of the detected face from the device. Values in the range
+ * [-1, 1] indicate a good capture.
+ * @param isCancellable Whether the ongoing face operation should be canceled.
+ */
+ public FaceDataFrame(
+ int acquiredInfo,
+ int vendorCode,
+ float pan,
+ float tilt,
+ float distance,
+ boolean isCancellable) {
+ mAcquiredInfo = acquiredInfo;
+ mVendorCode = vendorCode;
+ mPan = pan;
+ mTilt = tilt;
+ mDistance = distance;
+ mIsCancellable = isCancellable;
+ }
+
+ /**
+ * @return An integer corresponding to a known acquired message.
+ *
+ * @see android.hardware.biometrics.BiometricFaceConstants
+ */
+ public int getAcquiredInfo() {
+ return mAcquiredInfo;
+ }
+
+ /**
+ * @return An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@link
+ * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}.
+ *
+ * @see android.hardware.biometrics.BiometricFaceConstants
+ */
+ public int getVendorCode() {
+ return mVendorCode;
+ }
+
+ /**
+ * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getPan() {
+ return mPan;
+ }
+
+ /**
+ * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getTilt() {
+ return mTilt;
+ }
+
+ /**
+ * @return The distance of the detected face from the device. Values in the range [-1, 1]
+ * indicate a good capture.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
+ * @return Whether the ongoing face operation should be canceled.
+ */
+ public boolean isCancellable() {
+ return mIsCancellable;
+ }
+
+ private FaceDataFrame(@NonNull Parcel source) {
+ mAcquiredInfo = source.readInt();
+ mVendorCode = source.readInt();
+ mPan = source.readFloat();
+ mTilt = source.readFloat();
+ mDistance = source.readFloat();
+ mIsCancellable = source.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAcquiredInfo);
+ dest.writeInt(mVendorCode);
+ dest.writeFloat(mPan);
+ dest.writeFloat(mTilt);
+ dest.writeFloat(mDistance);
+ dest.writeBoolean(mIsCancellable);
+ }
+
+ public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() {
+ @Override
+ public FaceDataFrame createFromParcel(Parcel source) {
+ return new FaceDataFrame(source);
+ }
+
+ @Override
+ public FaceDataFrame[] newArray(int size) {
+ return new FaceDataFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java
new file mode 100644
index 000000000000..8415419577e2
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollCell.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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollCell implements Parcelable {
+ private final int mX;
+ private final int mY;
+ private final int mZ;
+
+ /**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @param x The horizontal coordinate of this cell.
+ * @param y The vertical coordinate of this cell.
+ * @param z The depth coordinate of this cell.
+ */
+ public FaceEnrollCell(int x, int y, int z) {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ /**
+ * @return The horizontal coordinate of this cell.
+ */
+ public int getX() {
+ return mX;
+ }
+
+ /**
+ * @return The vertical coordinate of this cell.
+ */
+ public int getY() {
+ return mY;
+ }
+
+ /**
+ * @return The depth coordinate of this cell.
+ */
+ public int getZ() {
+ return mZ;
+ }
+
+ private FaceEnrollCell(@NonNull Parcel source) {
+ mX = source.readInt();
+ mY = source.readInt();
+ mZ = source.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mX);
+ dest.writeInt(mY);
+ dest.writeInt(mZ);
+ }
+
+ public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() {
+ @Override
+ public FaceEnrollCell createFromParcel(Parcel source) {
+ return new FaceEnrollCell(source);
+ }
+
+ @Override
+ public FaceEnrollCell[] newArray(int size) {
+ return new FaceEnrollCell[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
new file mode 100644
index 000000000000..551139d240a3
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -0,0 +1,103 @@
+/*
+ * 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.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollFrame implements Parcelable {
+ @Nullable private final FaceEnrollCell mCell;
+ @FaceEnrollStage private final int mStage;
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @param cell The cell captured during this frame of enrollment, if any.
+ * @param stage An integer representing the current stage of enrollment.
+ * @param data Information about the current frame.
+ */
+ public FaceEnrollFrame(
+ @Nullable FaceEnrollCell cell,
+ @FaceEnrollStage int stage,
+ @NonNull FaceDataFrame data) {
+ mCell = cell;
+ mStage = stage;
+ mData = data;
+ }
+
+ /**
+ * @return The cell captured during this frame of enrollment, if any.
+ */
+ @Nullable
+ public FaceEnrollCell getCell() {
+ return mCell;
+ }
+
+ /**
+ * @return An integer representing the current stage of enrollment.
+ */
+ @FaceEnrollStage
+ public int getStage() {
+ return mStage;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceEnrollFrame(@NonNull Parcel source) {
+ mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+ mStage = source.readInt();
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mCell, flags);
+ dest.writeInt(mStage);
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() {
+ @Override
+ public FaceEnrollFrame createFromParcel(Parcel source) {
+ return new FaceEnrollFrame(source);
+ }
+
+ @Override
+ public FaceEnrollFrame[] newArray(int size) {
+ return new FaceEnrollFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
new file mode 100644
index 000000000000..03dba551aae2
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -0,0 +1,68 @@
+/*
+ * 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.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A stage that may occur during face enrollment.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+ FaceEnrollStage.FIRST_FRAME_RECEIVED,
+ FaceEnrollStage.WAITING_FOR_CENTERING,
+ FaceEnrollStage.HOLD_STILL_IN_CENTER,
+ FaceEnrollStage.ENROLLING_MOVEMENT_1,
+ FaceEnrollStage.ENROLLING_MOVEMENT_2,
+ FaceEnrollStage.ENROLLMENT_FINISHED
+})
+public @interface FaceEnrollStage {
+ /**
+ * Enrollment has just begun. No action is needed from the user yet.
+ */
+ int FIRST_FRAME_RECEIVED = 0;
+
+ /**
+ * The user must center their face in the frame.
+ */
+ int WAITING_FOR_CENTERING = 1;
+
+ /**
+ * The user must keep their face centered in the frame.
+ */
+ int HOLD_STILL_IN_CENTER = 2;
+
+ /**
+ * The user must follow a first set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_1 = 3;
+
+ /**
+ * The user must follow a second set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_2 = 4;
+
+ /**
+ * Enrollment has completed. No more action is needed from the user.
+ */
+ int ENROLLMENT_FINISHED = 5;
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 44a2e97e6f04..7e2be01feb01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1732,7 +1732,7 @@ public class InputMethodService extends AbstractInputMethodService {
// If app window has portrait orientation, regardless of what display orientation
// is, IME shouldn't use fullscreen-mode.
|| (mInputEditorInfo.internalImeOptions
- & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+ & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
return false;
}
return true;
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 84a2acc165c4..b016ed67c4d9 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -82,4 +82,5 @@ interface INetworkPolicyManager {
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
boolean isUidRestrictedOnMeteredNetworks(int uid);
+ boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ed169e75bd37..3e6237d99011 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -464,6 +464,31 @@ public class NetworkPolicyManager {
}
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ *
+ * @hide
+ */
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ try {
+ return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check that the given uid is restricted from doing networking on metered networks.
*
* @param uid The target uid.
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9c01f2..5e56164cc82c 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.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.
@@ -18,14 +18,14 @@ package android.net;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/** @hide */
@@ -60,16 +60,16 @@ public final class OemNetworkPreferences implements Parcelable {
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
@NonNull
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
@NonNull
- public SparseArray<List<String>> getNetworkPreferences() {
- return mNetworkMappings.clone();
+ public Map<String, Integer> getNetworkPreferences() {
+ return convertToUnmodifiableMap(mNetworkMappings);
}
- private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+ private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
Objects.requireNonNull(networkMappings);
- mNetworkMappings = networkMappings.clone();
+ mNetworkMappings = (Bundle) networkMappings.clone();
}
@Override
@@ -99,26 +99,45 @@ public final class OemNetworkPreferences implements Parcelable {
* @hide
*/
public static final class Builder {
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
public Builder() {
- mNetworkMappings = new SparseArray<>();
+ mNetworkMappings = new Bundle();
+ }
+
+ public Builder(@NonNull final OemNetworkPreferences preferences) {
+ Objects.requireNonNull(preferences);
+ mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
}
/**
- * Add a network preference for a list of packages.
+ * Add a network preference for a given package. Previously stored values for the given
+ * package will be overwritten.
*
- * @param preference the desired network preference to use
- * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use
- * the given preference
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+ * to use the given preference
+ * @param preference the desired network preference to use
* @return The builder to facilitate chaining.
*/
@NonNull
- public Builder addNetworkPreference(@OemNetworkPreference final int preference,
- @NonNull List<String> packages) {
- Objects.requireNonNull(packages);
- mNetworkMappings.put(preference,
- Collections.unmodifiableList(new ArrayList<>(packages)));
+ public Builder addNetworkPreference(@NonNull final String packageName,
+ @OemNetworkPreference final int preference) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.putInt(packageName, preference);
+ return this;
+ }
+
+ /**
+ * Remove a network preference for a given package.
+ *
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+ * remove a preference for.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder removeNetworkPreference(@NonNull final String packageName) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.remove(packageName);
return this;
}
@@ -131,6 +150,14 @@ public final class OemNetworkPreferences implements Parcelable {
}
}
+ private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+ final Map<String, Integer> networkPreferences = new HashMap<>();
+ for (final String key : bundle.keySet()) {
+ networkPreferences.put(key, bundle.getInt(key));
+ }
+ return Collections.unmodifiableMap(networkPreferences);
+ }
+
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@ public final class OemNetworkPreferences implements Parcelable {
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- dest.writeSparseArray(mNetworkMappings);
+ dest.writeBundle(mNetworkMappings);
}
@Override
@@ -187,7 +214,7 @@ public final class OemNetworkPreferences implements Parcelable {
@Override
public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
return new OemNetworkPreferences(
- in.readSparseArray(getClass().getClassLoader()));
+ in.readBundle(getClass().getClassLoader()));
}
};
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 77183ac21d09..ea1ce3728365 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1694,10 +1694,13 @@ public class UserManager {
}
/**
- * @hide
- * @return Whether the device is running in a headless system user mode. It means the headless
- * user (system user) runs system services and system UI, but is not associated with any real
- * person. Secondary users can be created to be associated with real person.
+ * Checks whether the device is running in a headless system user mode.
+ *
+ * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+ * services and some system UI, but it is not associated with any real person and additional
+ * users must be created to be associated with real persons.
+ *
+ * @return whether the device is running in a headless system user mode.
*/
public static boolean isHeadlessSystemUserMode() {
return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 7e7057fe56cb..0ff68fc582d8 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -304,20 +304,16 @@ public final class IncrementalManager {
}
/**
- * Called when a callback wants to stop listen to the loading progress of an installed package.
- * Decrease the count of the callbacks on the associated to the corresponding storage.
- * If the count becomes zero, unregister the storage listener.
+ * Called to stop all listeners from listening to loading progress of an installed package.
* @param codePath Path of the installed package
- * @return True if the package name and associated storage id are valid. False otherwise.
*/
- public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
- @NonNull IPackageLoadingProgressCallback callback) {
+ public void unregisterLoadingProgressCallbacks(@NonNull String codePath) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
// storage does not exist, package not installed
- return false;
+ return;
}
- return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
+ mLoadingProgressCallbacks.cleanUpCallbacks(storage);
}
private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
@@ -325,7 +321,6 @@ public final class IncrementalManager {
private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
new SparseArray<>();
- // TODO(b/165841827): disable callbacks when app state changes to fully loaded
public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
final int storageId = storage.getId();
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96f4be2..ad49ffd57dc2 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@ public abstract class ResumeOnRebootService extends Service {
* Implementation for wrapping the opaque blob used for resume-on-reboot prior to
* reboot. The service should not assume any structure of the blob to be wrapped. The
* implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
- * if it's unable to complete the action.
+ * if it's unable to complete the action due to retry-able errors (e.g network errors)
+ * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+ * (e.g corrupted blob).
*
* @param blob The opaque blob with size on the order of 100 bytes.
* @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@ public abstract class ResumeOnRebootService extends Service {
* this function after expiration should
* fail.
* @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to wrap the blob successfully.
+ * @throws IOException if the implementation is unable to wrap the blob successfully due to
+ * retry-able errors.
*/
@NonNull
public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@ public abstract class ResumeOnRebootService extends Service {
* operation would happen after reboot during direct boot mode (i.e before device is unlocked
* for the first time). The implementation should unwrap the wrapped blob in a reasonable time
* and returns the result or throw {@link IOException} if it's unable to complete the action
- * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
- * stale.
+ * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+ * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
*
* @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
* @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+ * due to retry-able errors.
*/
@NonNull
public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c368583c..a750b689ee02 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@ public abstract class ExternalStorageService extends Service {
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
*/
- public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+ public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
@@ -202,7 +202,7 @@ public abstract class ExternalStorageService extends Service {
RemoteCallback callback) {
mHandler.post(() -> {
try {
- onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+ onFreeCache(StorageManager.convert(volumeUuid), bytes);
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index f7710e6a82ce..ff03cc14e73b 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -18,6 +18,7 @@ package android.service.voice;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
@@ -44,6 +45,7 @@ public class VoiceInteractionServiceInfo {
private ServiceInfo mServiceInfo;
private String mSessionService;
private String mRecognitionService;
+ private String mHotwordDetectionService;
private String mSettingsActivity;
private boolean mSupportsAssist;
private boolean mSupportsLaunchFromKeyguard;
@@ -133,6 +135,8 @@ public class VoiceInteractionServiceInfo {
false);
mSupportsLocalInteraction = array.getBoolean(com.android.internal.
R.styleable.VoiceInteractionService_supportsLocalInteraction, false);
+ mHotwordDetectionService = array.getString(com.android.internal.R.styleable
+ .VoiceInteractionService_hotwordDetectionService);
array.recycle();
if (mSessionService == null) {
mParseError = "No sessionService specified";
@@ -181,4 +185,9 @@ public class VoiceInteractionServiceInfo {
public boolean getSupportsLocalInteraction() {
return mSupportsLocalInteraction;
}
+
+ @Nullable
+ public String getHotwordDetectionService() {
+ return mHotwordDetectionService;
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 41680647ad57..0ba1dfee16f3 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,8 +59,12 @@ import java.util.List;
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations. Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size). Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@ public final class Display {
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustments)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+ final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustements)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
}
}
@@ -1213,34 +1217,30 @@ public final class Display {
}
/**
- * Provides the largest {@link Point outSize} an app may expect in the current system state,
- * without subtracting any window decor.
+ * Gets the real size of the display without subtracting any window decor or
+ * applying any compatibility scale factors.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p>
+ * </p><p>
+ * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+ * report the same bounds except that certain areas of the display may not be available to
+ * windows created in the {@link WindowManager}'s {@link Context}.
+ *
+ * For example, imagine a device which has a multi-task mode that limits windows to half of the
+ * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+ * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+ * still reports the bounds of the whole display.
*
* @param outSize Set to the real size of the display.
+ *
+ * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- final Rect bounds = mResources.getConfiguration()
- .windowConfiguration.getMaxBounds();
- outSize.x = bounds.width();
- outSize.y = bounds.height();
- if (DEBUG) {
- Log.d(TAG, "getRealSize determined from max bounds: " + outSize
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@ public final class Display {
}
/**
- * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
- * system state, without subtracting any window decor.
+ * Gets display metrics based on the real size of this display.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@ public final class Display {
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- mDisplayInfo.getMaxBoundsMetrics(outMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
- mResources.getConfiguration());
- if (DEBUG) {
- Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@ public final class Display {
}
/**
- * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
- * display dimensions. The max bounds field may be smaller than the logical dimensions
- * when apps need to be sandboxed.
- * @return {@code true} when max bounds should be applied.
- */
- private boolean shouldReportMaxBounds() {
- if (mResources == null) {
- return false;
- }
- final Configuration config = mResources.getConfiguration();
- return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
- }
-
- /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a445041a1f2..2a00b5a2e513 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@ import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -616,29 +615,11 @@ public final class DisplayInfo implements Parcelable {
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
- /**
- * Populates {@code outMetrics} with details of the logical display. Bounds are limited
- * by the logical size of the display.
- *
- * @param outMetrics the {@link DisplayMetrics} to be populated
- * @param compatInfo the {@link CompatibilityInfo} to be applied
- * @param configuration the {@link Configuration}
- */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
- /**
- * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
- * {@link WindowConfiguration#getMaxBounds()}
- */
- public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
- Configuration configuration) {
- Rect bounds = configuration.windowConfiguration.getMaxBounds();
- getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
- }
-
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d2bc08..1f64fb8ca2ec 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@ oneway interface IRemoteAnimationRunner {
/**
* Called when the process needs to start the remote animation.
*
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+ in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336132a8..6a34a1520fa3 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType, translator);
InsetsAnimationThread.getHandler().post(() -> {
+ if (mControl.isCancelled()) {
+ return;
+ }
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
listener.onReady(mControl, types);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fe6b6e4d6fd1..219190f554ea 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -604,13 +604,13 @@ public class InsetsState implements Parcelable {
return Type.CAPTION_BAR;
case ITYPE_IME:
return Type.IME;
- case ITYPE_TOP_GESTURES:
- case ITYPE_BOTTOM_GESTURES:
case ITYPE_TOP_MANDATORY_GESTURES:
case ITYPE_BOTTOM_MANDATORY_GESTURES:
case ITYPE_LEFT_MANDATORY_GESTURES:
case ITYPE_RIGHT_MANDATORY_GESTURES:
return Type.MANDATORY_SYSTEM_GESTURES;
+ case ITYPE_TOP_GESTURES:
+ case ITYPE_BOTTOM_GESTURES:
case ITYPE_LEFT_GESTURES:
case ITYPE_RIGHT_GESTURES:
return Type.SYSTEM_GESTURES;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index acd25077fb5a..98b4acd302cb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -188,8 +188,6 @@ public final class SurfaceControl implements Parcelable {
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
long barrierObject, long frame);
- private static native void nativeReparentChildren(long transactionObj, long nativeObject,
- long newParentObject);
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
@@ -2970,15 +2968,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * @hide
- */
- public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
- checkPreconditions(sc);
- nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
- return this;
- }
-
- /**
* Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
* parent Surface.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 18ef80ce9772..036a703f178c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,7 +315,7 @@ public final class ViewRootImpl implements ViewParent,
* In that case we receive a call back from {@link ActivityThread} and this flag is used to
* preserve the initial value.
*
- * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+ * @see #performConfigurationChange(MergedConfiguration, boolean, int)
*/
private boolean mForceNextConfigUpdate;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index dd0ab6503a8a..170124e348c9 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -116,14 +116,6 @@ public final class WindowManagerGlobal {
*/
public static final int RELAYOUT_INSETS_PENDING = 0x1;
- /**
- * Flag for relayout: the client may be currently using the current surface,
- * so if it is to be destroyed as a part of the relayout the destroy must
- * be deferred until later. The client will call performDeferredDestroy()
- * when it is okay.
- */
- public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
-
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2df75f6570db..bde4cb7c4fc3 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -299,7 +299,7 @@ public class EditorInfo implements InputType, Parcelable {
* {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode.
* @hide
*/
- public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000;
+ public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001;
/**
* Generic unspecified type for {@link #imeOptions}.
@@ -321,7 +321,6 @@ public class EditorInfo implements InputType, Parcelable {
* 1 1 IME_ACTION_NEXT
* 11 IME_ACTION_DONE
* 111 IME_ACTION_PREVIOUS
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
* 1 IME_FLAG_NO_PERSONALIZED_LEARNING
* 1 IME_FLAG_NO_FULLSCREEN
* 1 IME_FLAG_NAVIGATE_PREVIOUS
@@ -356,7 +355,7 @@ public class EditorInfo implements InputType, Parcelable {
* Masks for {@link internalImeOptions}
*
* <pre>
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
+ * 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT
* |-------|-------|-------|-------|</pre>
*/
@@ -984,6 +983,7 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeInt(inputType);
dest.writeInt(imeOptions);
dest.writeString(privateImeOptions);
+ dest.writeInt(internalImeOptions);
TextUtils.writeToParcel(actionLabel, dest, flags);
dest.writeInt(actionId);
dest.writeInt(initialSelStart);
@@ -1019,6 +1019,7 @@ public class EditorInfo implements InputType, Parcelable {
res.inputType = source.readInt();
res.imeOptions = source.readInt();
res.privateImeOptions = source.readString();
+ res.internalImeOptions = source.readInt();
res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.actionId = source.readInt();
res.initialSelStart = source.readInt();
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 046f75fd37ac..2452e4c6d56a 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,7 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
/**
* <p>
@@ -52,6 +53,7 @@ import android.util.AttributeSet;
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class CheckBox extends CompoundButton {
public CheckBox(Context context) {
this(context, null);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 135ff9fcd989..63f8ee7528f2 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -27,11 +27,13 @@ import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
@@ -275,6 +277,7 @@ public abstract class CompoundButton extends Button implements Checkable {
* @param resId the resource identifier of the drawable
* @attr ref android.R.styleable#CompoundButton_button
*/
+ @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync")
public void setButtonDrawable(@DrawableRes int resId) {
final Drawable d;
if (resId != 0) {
@@ -285,6 +288,12 @@ public abstract class CompoundButton extends Button implements Checkable {
setButtonDrawable(d);
}
+ /** @hide **/
+ public Runnable setButtonDrawableAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setButtonDrawable(drawable);
+ }
+
/**
* Sets a drawable as the compound button image.
*
@@ -336,6 +345,23 @@ public abstract class CompoundButton extends Button implements Checkable {
}
/**
+ * Sets the button of this CompoundButton to the specified Icon.
+ *
+ * @param icon an Icon holding the desired button, or {@code null} to clear
+ * the button
+ */
+ @RemotableViewMethod(asyncImpl = "setButtonIconAsync")
+ public void setButtonIcon(@Nullable Icon icon) {
+ setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setButtonIconAsync(@Nullable Icon icon) {
+ Drawable button = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setButtonDrawable(button);
+ }
+
+ /**
* Applies a tint to the button drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -350,6 +376,7 @@ public abstract class CompoundButton extends Button implements Checkable {
* @see #setButtonTintList(ColorStateList)
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setButtonTintList(@Nullable ColorStateList tint) {
mButtonTintList = tint;
mHasButtonTint = true;
@@ -394,6 +421,7 @@ public abstract class CompoundButton extends Button implements Checkable {
* @see #getButtonTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setButtonTintBlendMode(@Nullable BlendMode tintMode) {
mButtonBlendMode = tintMode;
mHasButtonBlendMode = true;
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index a04d7c34c444..9b3503433e56 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -49,6 +50,7 @@ import com.android.internal.R;
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class RadioButton extends CompoundButton {
public RadioButton(Context context) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 4722fdc7818f..d445fdc01564 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -31,6 +31,7 @@ import android.view.ViewStructure;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -59,6 +60,7 @@ import com.android.internal.R;
* @see RadioButton
*
*/
+@RemoteView
public class RadioGroup extends LinearLayout {
private static final String LOG_TAG = RadioGroup.class.getSimpleName();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b47a0acc4fae..dfef7ca825a1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -132,6 +132,13 @@ import java.util.function.Consumer;
* <li>{@link android.widget.TextClock}</li>
* <li>{@link android.widget.TextView}</li>
* </ul>
+ * <p>As of API 31, the following widgets and layouts may also be used:</p>
+ * <ul>
+ * <li>{@link android.widget.CheckBox}</li>
+ * <li>{@link android.widget.RadioButton}</li>
+ * <li>{@link android.widget.RadioGroup}</li>
+ * <li>{@link android.widget.Switch}</li>
+ * </ul>
* <p>Descendants of these classes are not supported.</p>
*/
public class RemoteViews implements Parcelable, Filter {
@@ -185,6 +192,8 @@ public class RemoteViews implements Parcelable, Filter {
private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
private static final int COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG = 25;
+ private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26;
+ private static final int SET_RADIO_GROUP_CHECKED = 27;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
@@ -2552,6 +2561,87 @@ public class RemoteViews implements Parcelable, Filter {
}
}
+ private static class SetCompoundButtonCheckedAction extends Action {
+
+ private final boolean mChecked;
+
+ SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
+ this.viewId = viewId;
+ mChecked = checked;
+ }
+
+ SetCompoundButtonCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mChecked = in.readBoolean();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeBoolean(mChecked);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof CompoundButton)) {
+ Log.w(LOG_TAG, "Cannot set checked to view "
+ + viewId + " because it is not a CompoundButton");
+ return;
+ }
+
+ ((CompoundButton) target).setChecked(mChecked);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_COMPOUND_BUTTON_CHECKED_TAG;
+ }
+ }
+
+ private static class SetRadioGroupCheckedAction extends Action {
+
+ @IdRes private final int mCheckedId;
+
+ SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
+ this.viewId = viewId;
+ mCheckedId = checkedId;
+ }
+
+ SetRadioGroupCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mCheckedId = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeInt(mCheckedId);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof RadioGroup)) {
+ Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+ return;
+ }
+
+ ((RadioGroup) target).check(mCheckedId);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_RADIO_GROUP_CHECKED;
+ }
+ }
+
/**
* Create a new RemoteViews object that will display the views contained
* in the specified layout file.
@@ -2766,6 +2856,10 @@ public class RemoteViews implements Parcelable, Filter {
return new ResourceReflectionAction(parcel);
case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
return new ComplexUnitDimensionReflectionAction(parcel);
+ case SET_COMPOUND_BUTTON_CHECKED_TAG:
+ return new SetCompoundButtonCheckedAction(parcel);
+ case SET_RADIO_GROUP_CHECKED:
+ return new SetRadioGroupCheckedAction(parcel);
default:
throw new ActionException("Tag " + tag + " not found");
}
@@ -3846,6 +3940,26 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checked true to check the button, false to uncheck it.
+ */
+ public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
+ addAction(new SetCompoundButtonCheckedAction(viewId, checked));
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checkedId The unique id of the radio button to select in the group.
+ */
+ public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
+ addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
+ }
+
+ /**
* Provides an alternate layout ID, which can be used to inflate this view. This layout will be
* used by the host when the widgets displayed on a light-background where foreground elements
* and text can safely draw using a dark color without any additional background protection.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3295fd2ea1c3..d3600ef9f557 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@ import android.graphics.Rect;
import android.graphics.Region.Op;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.text.Layout;
@@ -48,12 +49,14 @@ import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.inspector.InspectableProperty;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -84,6 +87,7 @@ import com.android.internal.R;
* @attr ref android.R.styleable#Switch_thumbTextPadding
* @attr ref android.R.styleable#Switch_track
*/
+@RemoteView
public class Switch extends CompoundButton {
private static final int THUMB_ANIMATION_DURATION = 250;
@@ -441,6 +445,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_switchPadding
*/
+ @RemotableViewMethod
public void setSwitchPadding(int pixels) {
mSwitchPadding = pixels;
requestLayout();
@@ -466,6 +471,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_switchMinWidth
*/
+ @RemotableViewMethod
public void setSwitchMinWidth(int pixels) {
mSwitchMinWidth = pixels;
requestLayout();
@@ -491,6 +497,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_thumbTextPadding
*/
+ @RemotableViewMethod
public void setThumbTextPadding(int pixels) {
mThumbTextPadding = pixels;
requestLayout();
@@ -533,10 +540,17 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_track
*/
+ @RemotableViewMethod(asyncImpl = "setTrackResourceAsync")
public void setTrackResource(@DrawableRes int resId) {
setTrackDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setTrackResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setTrackDrawable(drawable);
+ }
+
/**
* Get the drawable used for the track that the switch slides within.
*
@@ -550,6 +564,23 @@ public class Switch extends CompoundButton {
}
/**
+ * Set the drawable used for the track that the switch slides within to the specified Icon.
+ *
+ * @param icon an Icon holding the desired track, or {@code null} to clear
+ * the track
+ */
+ @RemotableViewMethod(asyncImpl = "setTrackIconAsync")
+ public void setTrackIcon(@Nullable Icon icon) {
+ setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setTrackIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setTrackDrawable(track);
+ }
+
+ /**
* Applies a tint to the track drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -563,6 +594,7 @@ public class Switch extends CompoundButton {
* @see #getTrackTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setTrackTintList(@Nullable ColorStateList tint) {
mTrackTintList = tint;
mHasTrackTint = true;
@@ -607,6 +639,7 @@ public class Switch extends CompoundButton {
* @see #getTrackTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setTrackTintBlendMode(@Nullable BlendMode blendMode) {
mTrackBlendMode = blendMode;
mHasTrackTintMode = true;
@@ -686,10 +719,17 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_thumb
*/
+ @RemotableViewMethod(asyncImpl = "setThumbResourceAsync")
public void setThumbResource(@DrawableRes int resId) {
setThumbDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setThumbResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setThumbDrawable(drawable);
+ }
+
/**
* Get the drawable used for the switch "thumb" - the piece that the user
* can physically touch and drag along the track.
@@ -704,6 +744,24 @@ public class Switch extends CompoundButton {
}
/**
+ * Set the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track - to the specified Icon.
+ *
+ * @param icon an Icon holding the desired thumb, or {@code null} to clear
+ * the thumb
+ */
+ @RemotableViewMethod(asyncImpl = "setThumbIconAsync")
+ public void setThumbIcon(@Nullable Icon icon) {
+ setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setThumbIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setThumbDrawable(track);
+ }
+
+ /**
* Applies a tint to the thumb drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -717,6 +775,7 @@ public class Switch extends CompoundButton {
* @see #getThumbTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setThumbTintList(@Nullable ColorStateList tint) {
mThumbTintList = tint;
mHasThumbTint = true;
@@ -761,6 +820,7 @@ public class Switch extends CompoundButton {
* @see #getThumbTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setThumbTintBlendMode(@Nullable BlendMode blendMode) {
mThumbBlendMode = blendMode;
mHasThumbTintMode = true;
@@ -822,6 +882,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_splitTrack
*/
+ @RemotableViewMethod
public void setSplitTrack(boolean splitTrack) {
mSplitTrack = splitTrack;
invalidate();
@@ -852,6 +913,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_textOn
*/
+ @RemotableViewMethod
public void setTextOn(CharSequence textOn) {
mTextOn = textOn;
requestLayout();
@@ -875,6 +937,7 @@ public class Switch extends CompoundButton {
*
* @attr ref android.R.styleable#Switch_textOff
*/
+ @RemotableViewMethod
public void setTextOff(CharSequence textOff) {
mTextOff = textOff;
requestLayout();
@@ -889,6 +952,7 @@ public class Switch extends CompoundButton {
* @param showText {@code true} to display on/off text
* @attr ref android.R.styleable#Switch_showText
*/
+ @RemotableViewMethod
public void setShowText(boolean showText) {
if (mShowText != showText) {
mShowText = showText;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8cfbca88c596..796607065944 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8750,7 +8750,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) {
- outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT;
+ outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT;
}
if (isMultilineInputType(outAttrs.inputType)) {
// Multi-line text editors should always show an enter key.
diff --git a/core/java/com/android/internal/widget/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java
new file mode 100644
index 000000000000..0d9bf7168ebf
--- /dev/null
+++ b/core/java/com/android/internal/widget/DisableImageView.java
@@ -0,0 +1,68 @@
+/*
+ * 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.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
+ */
+@RemoteViews.RemoteView
+public class DisableImageView extends ImageView {
+
+ public DisableImageView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ // Apply the disabled filter
+ ColorMatrix brightnessMatrix = new ColorMatrix();
+ float brightnessF = 0.5f;
+ int brightnessI = (int) (255 * brightnessF);
+ // Brightness: C-new = C-old*(1-amount) + amount
+ float scale = 1f - brightnessF;
+ float[] mat = brightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = brightnessI;
+ mat[9] = brightnessI;
+ mat[14] = brightnessI;
+
+ ColorMatrix filterMatrix = new ColorMatrix();
+ filterMatrix.setSaturation(0);
+ filterMatrix.preConcat(brightnessMatrix);
+ setColorFilter(new ColorMatrixColorFilter(filterMatrix));
+ }
+}
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933e9a1c..d284d5167843 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@ per-file *Notification* = file:/services/core/java/com/android/server/notificati
per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index b4d8e506c61a..95999a716707 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@ import android.content.pm.IPackageManager;
import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.RemoteException;
@@ -74,7 +73,6 @@ public class BootReceiver extends BroadcastReceiver {
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
- private static final File TOMBSTONE_DIR = new File("/data/tombstones");
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
@@ -85,9 +83,6 @@ public class BootReceiver extends BroadcastReceiver {
private static final String OLD_UPDATER_CLASS =
"com.google.android.systemupdater.SystemUpdateReceiver";
- // Keep a reference to the observer so the finalizer doesn't disable it.
- private static FileObserver sTombstoneObserver = null;
-
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -153,7 +148,7 @@ public class BootReceiver extends BroadcastReceiver {
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
- private String getPreviousBootHeaders() {
+ private static String getPreviousBootHeaders() {
try {
return FileUtils.readTextFile(lastHeaderFile, 0, null);
} catch (IOException e) {
@@ -161,7 +156,7 @@ public class BootReceiver extends BroadcastReceiver {
}
}
- private String getCurrentBootHeaders() throws IOException {
+ private static String getCurrentBootHeaders() throws IOException {
return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
@@ -175,7 +170,7 @@ public class BootReceiver extends BroadcastReceiver {
}
- private String getBootHeadersToLogAndUpdate() throws IOException {
+ private static String getBootHeadersToLogAndUpdate() throws IOException {
final String oldHeaders = getPreviousBootHeaders();
final String newHeaders = getCurrentBootHeaders();
@@ -247,38 +242,27 @@ public class BootReceiver extends BroadcastReceiver {
logFsMountTime();
addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
logSystemServerShutdownTimeMetrics();
+ writeTimestamps(timestamps);
+ }
- // Scan existing tombstones (in case any new ones appeared)
- File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
- for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
- if (tombstoneFiles[i].isFile()) {
- addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
- LOG_SIZE, "SYSTEM_TOMBSTONE");
- }
+ /**
+ * Add a tombstone to the DropBox.
+ *
+ * @param ctx Context
+ * @param tombstone path to the tombstone
+ */
+ public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+ HashMap<String, Long> timestamps = readTimestamps();
+ try {
+ final String headers = getBootHeadersToLogAndUpdate();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't log tombstone", e);
}
-
writeTimestamps(timestamps);
-
- // Start watching for new tombstone files; will record them as they occur.
- // This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
- @Override
- public void onEvent(int event, String path) {
- HashMap<String, Long> timestamps = readTimestamps();
- try {
- File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile() && file.getName().startsWith("tombstone_")) {
- addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Can't log tombstone", e);
- }
- writeTimestamps(timestamps);
- }
- };
-
- sTombstoneObserver.startWatching();
}
private static void addLastkToDropBox(
@@ -761,7 +745,7 @@ public class BootReceiver extends BroadcastReceiver {
}
}
- private void writeTimestamps(HashMap<String, Long> timestamps) {
+ private static void writeTimestamps(HashMap<String, Long> timestamps) {
synchronized (sFile) {
final FileOutputStream stream;
try {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0b48e72a4acd..8edc8a186c59 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,9 +207,9 @@ cc_library_shared {
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "audioflinger-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 05fcaec82f84..859730872e8c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1409,16 +1409,6 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong transac
transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
}
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject,
- jlong newParentObject) {
-
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->reparentChildren(ctrl, newParent);
-}
-
static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
jlong newParentObject) {
@@ -1838,8 +1828,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetProtectedContentSupport },
{"nativeDeferTransactionUntil", "(JJJJ)V",
(void*)nativeDeferTransactionUntil },
- {"nativeReparentChildren", "(JJJ)V",
- (void*)nativeReparentChildren } ,
{"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
{"nativeCaptureDisplay",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 269ccbbbfaec..827bf7b70cbc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,10 +690,6 @@
<!-- Made protected in S (was added in R) -->
<protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
- <!-- Added in S -->
- <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
- <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2539,7 +2535,7 @@
<permission android:name="android.permission.REAL_GET_TASKS"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
android:protectionLevel="signature|privileged|recents" />
@@ -5431,7 +5427,7 @@
<permission android:name="android.permission.INPUT_CONSUMER"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to control the system's device state managed by the
+ <!-- @hide @TestApi Allows an application to control the system's device state managed by the
{@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
devices this would grant access to toggle between the folded and unfolded states. -->
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 39e1bbb467b4..9e1692f23a1e 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -22,7 +22,8 @@ Copyright (C) 2015 The Android Open Source Project
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
- <ImageView android:id="@+id/work_widget_app_icon"
+ <com.android.internal.widget.DisableImageView
+ android:id="@+id/work_widget_app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index eb867090f8ba..14df77527a08 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8500,6 +8500,9 @@
interaction requests from an Activity. This flag is new in
{@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
<attr name="supportsLocalInteraction" format="boolean" />
+ <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
+ @hide @SystemApi -->
+ <attr name="hotwordDetectionService" format="string" />
</declare-styleable>
<!-- Use <code>voice-enrollment-application</code>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a7e8f2adab9c..30cfb8986035 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,6 +3059,8 @@
<public name="hand_second" />
<public name="memtagMode" />
<public name="nativeHeapZeroInit" />
+ <!-- @hide @SystemApi -->
+ <public name="hotwordDetectionService" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 36f01f9a951d..e7fdfb8c19e9 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -17,8 +17,11 @@
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 android.os.IBinder;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -30,6 +33,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -41,6 +45,9 @@ import java.util.Set;
@RunWith(JUnit4.class)
@SmallTest
public final class DeviceStateManagerGlobalTest {
+ private static final int DEFAULT_DEVICE_STATE = 0;
+ private static final int OTHER_DEVICE_STATE = 1;
+
private TestDeviceStateManagerService mService;
private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
@@ -52,7 +59,7 @@ public final class DeviceStateManagerGlobalTest {
@Test
public void registerListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener1 = new TestDeviceStateListener();
TestDeviceStateListener listener2 = new TestDeviceStateListener();
@@ -61,28 +68,58 @@ public final class DeviceStateManagerGlobalTest {
ConcurrentUtils.DIRECT_EXECUTOR);
mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener1.getLastReportedState().intValue());
- assertEquals(0, listener2.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
- mService.setDeviceState(1);
- assertEquals(1, listener1.getLastReportedState().intValue());
- assertEquals(1, listener2.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
}
@Test
public void unregisterListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener = new TestDeviceStateListener();
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
- mService.setDeviceState(1);
- assertEquals(0, listener.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+ }
+
+ @Test
+ public void submittingRequestRegisteredCallback() {
+ assertTrue(mService.mCallbacks.isEmpty());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertFalse(mService.mCallbacks.isEmpty());
+ }
+
+ @Test
+ public void submitRequest() {
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
+
+ TestDeviceStateListener listener = new TestDeviceStateListener();
+ mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ mDeviceStateManagerGlobal.cancelRequest(request);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
}
private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
@@ -100,8 +137,23 @@ public final class DeviceStateManagerGlobalTest {
}
}
- private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
- private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+ private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
+ public static final class Request {
+ public final IBinder token;
+ public final int state;
+ public final int flags;
+
+ private Request(IBinder token, int state, int flags) {
+ this.token = token;
+ this.state = state;
+ this.flags = flags;
+ }
+ }
+
+ private int mBaseState = DEFAULT_DEVICE_STATE;
+ private int mMergedState = DEFAULT_DEVICE_STATE;
+ private ArrayList<Request> mRequests = new ArrayList<>();
+
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
@Override
@@ -112,19 +164,86 @@ public final class DeviceStateManagerGlobalTest {
mCallbacks.add(callback);
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
}
- public void setDeviceState(int deviceState) {
- boolean stateChanged = mDeviceState != deviceState;
- mDeviceState = deviceState;
- if (stateChanged) {
+ @Override
+ public int[] getSupportedDeviceStates() {
+ return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
+ }
+
+ @Override
+ public void requestState(IBinder token, int state, int flags) {
+ if (!mRequests.isEmpty()) {
+ final Request topRequest = mRequests.get(mRequests.size() - 1);
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestSuspended(topRequest.token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mRequests.add(request);
+ notifyStateChangedIfNeeded();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ @Override
+ public void cancelRequest(IBinder token) {
+ int index = -1;
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.get(i).token.equals(token)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ throw new IllegalArgumentException("Unknown request: " + token);
+ }
+
+ mRequests.remove(index);
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ notifyStateChangedIfNeeded();
+ }
+
+ public void setBaseState(int state) {
+ mBaseState = state;
+ notifyStateChangedIfNeeded();
+ }
+
+ private void notifyStateChangedIfNeeded() {
+ final int originalMergedState = mMergedState;
+
+ if (!mRequests.isEmpty()) {
+ mMergedState = mRequests.get(mRequests.size() - 1).state;
+ } else {
+ mMergedState = mBaseState;
+ }
+
+ if (mMergedState != originalMergedState) {
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35b1194..000000000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +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 android.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
- private static final int APP_WIDTH = 272;
- private static final int APP_HEIGHT = 700;
- // Tablet size device, ROTATION_0 corresponds to portrait.
- private static final int LOGICAL_WIDTH = 700;
- private static final int LOGICAL_HEIGHT = 1800;
-
- // Bounds of the app when the device is in portrait mode.
- private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
- private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
- private StaticMockitoSession mMockitoSession;
-
- private DisplayManagerGlobal mDisplayManagerGlobal;
- private Context mApplicationContext;
- private DisplayInfo mDisplayInfo = new DisplayInfo();
-
- @Before
- public void setupTests() {
- mMockitoSession = mockitoSession()
- .mockStatic(DisplayManagerGlobal.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
-
- // Ensure no adjustments are set before each test.
- mApplicationContext = ApplicationProvider.getApplicationContext();
- DisplayAdjustments displayAdjustments =
- mApplicationContext.getResources().getDisplayAdjustments();
- displayAdjustments.setFixedRotationAdjustments(null);
- mApplicationContext.getResources().overrideDisplayAdjustments(null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
- null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
- null);
- mDisplayInfo.rotation = ROTATION_0;
-
- mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
- doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
- }
-
- @After
- public void teardownTests() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
- }
- Mockito.framework().clearInlineMocks();
- }
-
- @Test
- public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testConstructor_defaultResources_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- mApplicationContext.getResources().getDisplayAdjustments());
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- // GIVEN display is constructed with display adjustments.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- displayAdjustments);
- // THEN rotation is not adjusted since no override was set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is not adjusted since no override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is adjusted since an override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_90);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated with an override.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
- }
-
- // Given rotated display dimensions, calculate the letterboxed app bounds.
- private static Rect buildAppBounds(int displayWidth, int displayHeight) {
- final int midWidth = displayWidth / 2;
- final int left = midWidth - (APP_WIDTH / 2);
- final int right = midWidth + (APP_WIDTH / 2);
- final int midHeight = displayHeight / 2;
- // Coordinate system starts at top left.
- final int top = midHeight - (APP_HEIGHT / 2);
- final int bottom = midHeight + (APP_HEIGHT / 2);
- return new Rect(left, top, right, bottom);
- }
-
- private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_90;
- // Flip width & height assignment since the device is rotated.
- displayInfo.logicalWidth = LOGICAL_HEIGHT;
- displayInfo.logicalHeight = LOGICAL_WIDTH;
- }
-
- private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_0;
- displayInfo.logicalWidth = LOGICAL_WIDTH;
- displayInfo.logicalHeight = LOGICAL_HEIGHT;
- }
-
- /**
- * Set max bounds to be sandboxed to the app bounds, indicating the app is in
- * size compat mode or letterbox.
- */
- private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
- resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
- }
-
- /**
- * Do not compare entire display info, since it is updated to match display the test is run on.
- */
- private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- assertThat(actual.displayId).isEqualTo(expected.displayId);
- assertThat(actual.rotation).isEqualTo(expected.rotation);
- assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
- assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeIsLandscape(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- // Flip the width and height check since the device is rotated.
- assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
- }
-
- private static void verifyRealMetricsIsLandscape(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- // Flip the width and height check since the device is rotated.
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
- }
-
- private static void verifyRealSizeIsPortrait(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
- }
-
- private static void verifyRealMetricsIsPortrait(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
- }
-
- private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
- assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
- }
-
- private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
- Resources resources, @Surface.Rotation int rotation) {
- FixedRotationAdjustments fixedRotationAdjustments =
- setFixedRotationAdjustments(resources, rotation);
- resources.overrideDisplayAdjustments(
- buildOverrideRotationAdjustments(fixedRotationAdjustments));
- return fixedRotationAdjustments;
- }
-
- private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
- @Surface.Rotation int rotation) {
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
- return fixedRotationAdjustments;
- }
-
- private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
- FixedRotationAdjustments fixedRotationAdjustments) {
- return consumedDisplayAdjustments
- -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- }
-}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296123c6..222c9bdf2cb4 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "123161180": {
+ "message": "SEVER CHILDREN",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2137,12 +2143,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "332390227": {
- "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,12 +2623,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
- "910200295": {
- "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 005a72661091..35e6b8595c2a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@ import android.provider.FontsContract;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.LruCache;
@@ -67,7 +68,6 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -147,7 +147,7 @@ public class Typeface {
*/
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
@UnsupportedAppUsage(trackingBug = 123769347)
- static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+ static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
// DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
static ByteBuffer sSystemFontMapBuffer = null;
@@ -1231,7 +1231,7 @@ public class Typeface {
/** @hide */
@VisibleForTesting
public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
- Map<String, Typeface> fontMap = new HashMap<>();
+ Map<String, Typeface> fontMap = new ArrayMap<>();
int typefacesBytesCount = buffer.getInt();
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
if (nativePtrs == null) {
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c29c194861f1..77f86fe726f3 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -20,15 +20,16 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.FontConfig;
+import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.ArrayList;
-import java.util.HashSet;
/**
* A font family class can be used for creating Typeface.
@@ -68,7 +69,9 @@ public final class FontFamily {
nGetReleaseNativeFamily());
private final ArrayList<Font> mFonts = new ArrayList<>();
- private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+ // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for
+ // initial capacity.
+ private final SparseIntArray mStyles = new SparseIntArray(4);
/**
* Constructs a builder.
@@ -77,7 +80,7 @@ public final class FontFamily {
*/
public Builder(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- mStyleHashSet.add(makeStyleIdentifier(font));
+ mStyles.append(makeStyleIdentifier(font), 0);
mFonts.add(font);
}
@@ -97,9 +100,11 @@ public final class FontFamily {
*/
public @NonNull Builder addFont(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+ int key = makeStyleIdentifier(font);
+ if (mStyles.indexOfKey(key) >= 0) {
throw new IllegalArgumentException(font + " has already been added");
}
+ mStyles.append(key, 0);
mFonts.add(font);
return this;
}
@@ -120,7 +125,7 @@ public final class FontFamily {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
- final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr);
+ final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
@@ -139,15 +144,11 @@ public final class FontFamily {
}
private final ArrayList<Font> mFonts;
- private final String mLangTags;
- private final int mVariant;
private final long mNativePtr;
// Use Builder instead.
- private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) {
+ private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
mFonts = fonts;
- mLangTags = langTags;
- mVariant = variant;
mNativePtr = ptr;
}
@@ -157,7 +158,7 @@ public final class FontFamily {
* @return a BCP-47 compliant language tag.
*/
public @Nullable String getLangTags() {
- return mLangTags;
+ return nGetLangTags(mNativePtr);
}
/**
@@ -165,7 +166,7 @@ public final class FontFamily {
* @return a family variant
*/
public int getVariant() {
- return mVariant;
+ return nGetVariant(mNativePtr);
}
/**
@@ -191,4 +192,10 @@ public final class FontFamily {
public long getNativePtr() {
return mNativePtr;
}
+
+ @FastNative
+ private static native String nGetLangTags(long family);
+
+ @CriticalNative
+ private static native int nGetVariant(long family);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index c166e12fc6bf..904085feb8cb 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@ import android.graphics.FontListParser;
import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -36,8 +37,6 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -76,7 +75,7 @@ public final class SystemFonts {
if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
sAvailableFonts = collectAllFonts();
} else {
- Set<Font> set = new HashSet<>();
+ Set<Font> set = new ArraySet<>();
for (FontFamily[] items : sFamilyMap.values()) {
for (FontFamily family : items) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -96,7 +95,7 @@ public final class SystemFonts {
FontConfig fontConfig = getSystemPreinstalledFontConfig();
Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
- Set<Font> res = new HashSet<>();
+ Set<Font> res = new ArraySet<>();
for (FontFamily[] families : map.values()) {
for (FontFamily family : families) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -218,7 +217,7 @@ public final class SystemFonts {
}
private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
- @NonNull HashMap<String, ByteBuffer> bufferCache,
+ @NonNull ArrayMap<String, ByteBuffer> bufferCache,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
final String familyName = xmlFamily.getName();
final FontFamily family = createFontFamily(
@@ -284,8 +283,8 @@ public final class SystemFonts {
*/
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
- final Map<String, FontFamily[]> fallbackMap = new HashMap<>();
- final HashMap<String, ByteBuffer> bufferCache = new HashMap<>();
+ final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -326,7 +325,7 @@ public final class SystemFonts {
public static Map<String, Typeface> buildSystemTypefaces(
FontConfig fontConfig,
Map<String, FontFamily[]> fallbackMap) {
- final HashMap<String, Typeface> result = new HashMap<>();
+ final ArrayMap<String, Typeface> result = new ArrayMap<>();
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index fa4f8b1674d1..3ebca6ad302d 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
import android.security.KeyStore;
import android.security.keymaster.KeymasterDefs;
@@ -874,9 +876,18 @@ public abstract class KeyProperties {
* which it must be configured in SEPolicy.
* @hide
*/
+ @SystemApi
public static final int NAMESPACE_APPLICATION = -1;
/**
+ * The namespace identifier for the WIFI Keystore namespace.
+ * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+ * @hide
+ */
+ @SystemApi
+ public static final int NAMESPACE_WIFI = 102;
+
+ /**
* For legacy support, translate namespaces into known UIDs.
* @hide
*/
@@ -884,6 +895,8 @@ public abstract class KeyProperties {
switch (namespace) {
case NAMESPACE_APPLICATION:
return KeyStore.UID_SELF;
+ case NAMESPACE_WIFI:
+ return Process.WIFI_UID;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
@@ -900,6 +913,8 @@ public abstract class KeyProperties {
switch (uid) {
case KeyStore.UID_SELF:
return NAMESPACE_APPLICATION;
+ case Process.WIFI_UID:
+ return NAMESPACE_WIFI;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 75ac61a22cab..e1011155248e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -352,14 +352,17 @@ public class AndroidKeyStoreProvider extends Provider {
try {
response = keyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
- if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
- throw new KeyPermanentlyInvalidatedException(
- "User changed or deleted their auth credentials",
- e);
- } else {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(e);
+ switch (e.getErrorCode()) {
+ case ResponseCode.KEY_NOT_FOUND:
+ return null;
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ throw new KeyPermanentlyInvalidatedException(
+ "User changed or deleted their auth credentials",
+ e);
+ default:
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(e);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689be7e2..255e4d2c0d44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.animation
-import android.os.Looper
import android.util.ArrayMap
import android.util.Log
import android.view.View
@@ -847,7 +846,7 @@ class PhysicsAnimator<T> private constructor (target: T) {
* pass to [spring].
*/
data class SpringConfig internal constructor(
- internal var stiffness: Float,
+ var stiffness: Float,
internal var dampingRatio: Float,
internal var startVelocity: Float = 0f,
internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@ class PhysicsAnimator<T> private constructor (target: T) {
*/
data class FlingConfig internal constructor(
internal var friction: Float,
- internal var min: Float,
- internal var max: Float,
+ var min: Float,
+ var max: Float,
internal var startVelocity: Float
) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 39510e55139a..d54be0e62527 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1478,6 +1478,9 @@ public class BubbleStackView extends FrameLayout
* Update bubble order and pointer position.
*/
public void updateBubbleOrder(List<Bubble> bubbles) {
+ if (isExpansionAnimating()) {
+ return;
+ }
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
@@ -1662,6 +1665,7 @@ public class BubbleStackView extends FrameLayout
}
beforeExpandedViewAnimation();
+ updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
updatePointerPosition();
@@ -1875,7 +1879,7 @@ public class BubbleStackView extends FrameLayout
mExpandedBubble));
}
updateOverflowVisibility();
-
+ updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
previouslySelected.setContentVisibility(false);
@@ -2623,7 +2627,6 @@ public class BubbleStackView extends FrameLayout
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca27328f192..c0ab20d9249f 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "WMShellUnitTests",
- srcs: ["**/*.java"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
static_libs: [
"WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed26a82..17ed396987af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.animation.PhysicsAnimator.EndListener
import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5496ec..dd1a6a5a281e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@ import android.content.pm.LauncherApps
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe536411d5ed..9f1ee6c92700 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@ import android.view.MotionEvent
import android.view.View
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
/** Incrementing value for fake MotionEvent timestamps. */
private var time = 0L
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e52766f2ef..a07723f39b0c 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -83,6 +83,23 @@ static jlong FontFamily_Builder_GetReleaseFunc(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseFontFamily);
}
+// FastNative
+static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ uint32_t localeListId = family->family->localeListId();
+ if (localeListId == 0) {
+ return nullptr;
+ }
+ std::string langTags = minikin::getLocaleString(localeListId);
+ return env->NewStringUTF(langTags.c_str());
+}
+
+// CriticalNative
+static jint FontFamily_getVariant(jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ return static_cast<jint>(family->family->variant());
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
@@ -93,9 +110,16 @@ static const JNINativeMethod gFontFamilyBuilderMethods[] = {
{ "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
};
+static const JNINativeMethod gFontFamilyMethods[] = {
+ {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
+ {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
+};
+
int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
- gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+ gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods,
+ NELEM(gFontFamilyMethods));
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 8525e9979aef..ee0be010c233 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -65,6 +65,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -245,6 +246,8 @@ public class Tuner implements AutoCloseable {
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
+ private static final int FILTER_CLEANUP_THRESHOLD = 256;
+
/** @hide */
@IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
@Retention(RetentionPolicy.SOURCE)
@@ -1208,6 +1211,15 @@ public class Tuner implements AutoCloseable {
synchronized (mFilters) {
WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
mFilters.add(weakFilter);
+ if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) {
+ Iterator<WeakReference<Filter>> iterator = mFilters.iterator();
+ while (iterator.hasNext()) {
+ WeakReference<Filter> wFilter = iterator.next();
+ if (wFilter.get() == null) {
+ iterator.remove();
+ }
+ }
+ }
}
}
return filter;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 67a2c49e5746..65b64d7e8df3 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,8 +31,8 @@ cc_library_shared {
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroid_runtime",
"libaudioclient",
"libnativehelper",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ec84d9d2265..eee9f1e08131 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3786,7 +3786,7 @@ static jint android_media_tv_Tuner_read_filter_fmq(
jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
return -1;
}
- int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+ int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
env->ReleaseByteArrayElements(buffer, dst, 0);
return (jint) realReadSize;
}
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 1a2f8c065bd3..748d45808932 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -88,12 +88,19 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize,
}
sp<TimeFilterClient> DemuxClient::openTimeFilter() {
- // TODO: pending aidl interface
+ if (mTunerDemux != NULL) {
+ shared_ptr<ITunerTimeFilter> tunerTimeFilter;
+ Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new TimeFilterClient(tunerTimeFilter);
+ }
if (mDemux != NULL) {
sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
if (hidlTimeFilter != NULL) {
- sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+ sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
return timeFilterClient;
}
@@ -103,7 +110,14 @@ sp<TimeFilterClient> DemuxClient::openTimeFilter() {
}
int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int hwId;
+ Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_HW_ID;
+ }
+ return hwId;
+ }
if (mDemux != NULL) {
uint32_t avSyncHwId;
@@ -119,11 +133,18 @@ int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
}
}
- return -1;
+ return INVALID_AV_SYNC_HW_ID;
}
long DemuxClient::getAvSyncTime(int avSyncHwId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int64_t time;
+ Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_TIME;
+ }
+ return time;
+ }
if (mDemux != NULL) {
uint64_t time;
@@ -138,7 +159,7 @@ long DemuxClient::getAvSyncTime(int avSyncHwId) {
}
}
- return -1;
+ return INVALID_AV_SYNC_TIME;
}
sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
@@ -167,7 +188,10 @@ sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClient
}
Result DemuxClient::connectCiCam(int ciCamId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->connectCiCam(ciCamId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
@@ -177,7 +201,10 @@ Result DemuxClient::connectCiCam(int ciCamId) {
}
Result DemuxClient::disconnectCiCam() {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->disconnectCiCam();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->disconnectCiCam();
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 463944a7dc00..31eb35a1a56d 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -31,7 +31,9 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
@@ -39,6 +41,9 @@ using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
using namespace std;
+const int64_t INVALID_AV_SYNC_TIME = -1;
+const int INVALID_AV_SYNC_HW_ID = -1;
+
namespace android {
struct DemuxClient : public RefBase {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 6b788170a944..8b4ca371056e 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -18,6 +18,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
+#include <fmq/ConvertMQDescriptors.h>
#include <utils/Log.h>
#include "FilterClient.h"
@@ -34,7 +35,7 @@ using ::aidl::android::media::tv::tuner::TunerFilterSectionTableInfo;
using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-
+using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
@@ -68,18 +69,12 @@ void FilterClient::setHidlFilter(sp<IFilter> filter) {
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
}
-int FilterClient::read(uint8_t* buffer, int size) {
- // TODO: pending aidl interface
-
- if (mFilter != NULL) {
- Result res = getFilterMq();
- if (res != Result::SUCCESS) {
- return -1;
- }
- return copyData(buffer, size);
+int FilterClient::read(int8_t* buffer, int size) {
+ Result res = getFilterMq();
+ if (res != Result::SUCCESS) {
+ return -1;
}
-
- return -1;
+ return copyData(buffer, size);
}
SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
@@ -106,7 +101,10 @@ Result FilterClient::configure(DemuxFilterSettings configure) {
}
Result FilterClient::configureMonitorEvent(int monitorEventType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureMonitorEvent(monitorEventType);
@@ -116,7 +114,10 @@ Result FilterClient::configureMonitorEvent(int monitorEventType) {
}
Result FilterClient::configureIpFilterContextId(int cid) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureIpFilterContextId(cid);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureIpCid(cid);
@@ -126,7 +127,19 @@ Result FilterClient::configureIpFilterContextId(int cid) {
}
Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ int type;
+ switch (avStreamType.getDiscriminator()) {
+ case AvStreamType::hidl_discriminator::audio:
+ type = (int)avStreamType.audio();
+ break;
+ case AvStreamType::hidl_discriminator::video:
+ type = (int)avStreamType.video();
+ break;
+ }
+ Status s = mTunerFilter->configureAvStreamType(type);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureAvStreamType(avStreamType);
@@ -228,7 +241,10 @@ Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId)
}
Result FilterClient::setDataSource(sp<FilterClient> filterClient){
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
sp<IFilter> sourceFilter = filterClient->getHalFilter();
@@ -687,10 +703,10 @@ void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& fil
void TunerFilterCallback::getHidlMediaEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
.get<TunerFilterEvent::media>().avMemory));
- event.events.resize(i + 1);
event.events[i].media({
.avMemory = handle,
.streamId = static_cast<DemuxStreamId>(filterEvents[i]
@@ -736,9 +752,9 @@ void TunerFilterCallback::getHidlMediaEvent(
void TunerFilterCallback::getHidlSectionEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto section = filterEvents[i].get<TunerFilterEvent::section>();
- event.events.resize(i + 1);
event.events[i].section({
.tableId = static_cast<uint16_t>(section.tableId),
.version = static_cast<uint16_t>(section.version),
@@ -750,9 +766,9 @@ void TunerFilterCallback::getHidlSectionEvent(
void TunerFilterCallback::getHidlPesEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
- event.events.resize(i + 1);
event.events[i].pes({
.streamId = static_cast<DemuxStreamId>(pes.streamId),
.dataLength = static_cast<uint16_t>(pes.dataLength),
@@ -763,9 +779,10 @@ void TunerFilterCallback::getHidlPesEvent(
void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
- event.events.resize(i + 1);
event.events[i].tsRecord({
.tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
.byteNumber = static_cast<uint64_t>(ts.byteNumber),
@@ -787,7 +804,6 @@ void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& f
break;
}
- eventExt.events.resize(i + 1);
if (ts.isExtended) {
eventExt.events[i].tsRecord({
.pts = static_cast<uint64_t>(ts.pts),
@@ -801,15 +817,15 @@ void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& f
void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
- event.events.resize(i + 1);
event.events[i].mmtpRecord({
.scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
.byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
});
- eventExt.events.resize(i + 1);
if (mmtp.isExtended) {
eventExt.events[i].mmtpRecord({
.pts = static_cast<uint64_t>(mmtp.pts),
@@ -825,9 +841,9 @@ void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>&
void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto download = filterEvents[i].get<TunerFilterEvent::download>();
- event.events.resize(i + 1);
event.events[i].download({
.itemId = static_cast<uint32_t>(download.itemId),
.mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
@@ -840,9 +856,9 @@ void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& f
void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
- event.events.resize(i + 1);
event.events[i].ipPayload({
.dataLength = static_cast<uint16_t>(ip.dataLength),
});
@@ -851,15 +867,15 @@ void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>&
void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
- event.events.resize(i + 1);
event.events[i].temi({
.pts = static_cast<uint64_t>(temi.pts),
.descrTag = static_cast<uint8_t>(temi.descrTag),
});
- vector<uint8_t> descrData(temi.descrData.size());
- copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+ hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
+ event.events[i].temi().descrData = descrData;
}
}
@@ -891,29 +907,43 @@ void TunerFilterCallback::getHidlRestartEvent(const vector<TunerFilterEvent>& fi
}
Result FilterClient::getFilterMq() {
- if (mFilter == NULL) {
- return Result::INVALID_STATE;
- }
-
if (mFilterMQ != NULL) {
return Result::SUCCESS;
}
- Result getQueueDescResult = Result::UNKNOWN_ERROR;
- MQDescriptorSync<uint8_t> filterMQDesc;
- mFilter->getQueueDesc(
- [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
- filterMQDesc = desc;
- getQueueDescResult = r;
- });
- if (getQueueDescResult == Result::SUCCESS) {
- mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
- EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ AidlMQDesc aidlMqDesc;
+ Result res = Result::UNAVAILABLE;
+
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
+ res = ClientHelper::getServiceSpecificErrorCode(s);
+ if (res == Result::SUCCESS) {
+ mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
+ return res;
+ }
+
+ if (mFilter != NULL) {
+ MQDescriptorSync<uint8_t> filterMQDesc;
+ mFilter->getQueueDesc(
+ [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+ filterMQDesc = desc;
+ res = r;
+ });
+ if (res == Result::SUCCESS) {
+ AidlMQDesc aidlMQDesc;
+ unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+ filterMQDesc, &aidlMQDesc);
+ mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
}
- return getQueueDescResult;
+
+ return res;
}
-int FilterClient::copyData(uint8_t* buffer, int size) {
+int FilterClient::copyData(int8_t* buffer, int size) {
if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
return -1;
}
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 21919ac10282..bbabc282464a 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -25,12 +25,14 @@
#include <android/hardware/tv/tuner/1.1/IFilter.h>
#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/AidlMessageQueue.h>
#include <fmq/MessageQueue.h>
#include "ClientHelper.h"
#include "FilterClientCallback.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
@@ -69,10 +71,13 @@ using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
using namespace std;
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
namespace android {
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
struct SharedHandleInfo {
native_handle_t* sharedHandle;
uint64_t size;
@@ -139,7 +144,7 @@ public:
*
* @return the actual reading size. -1 if failed to read.
*/
- int read(uint8_t* buffer, int size);
+ int read(int8_t* buffer, int size);
/**
* Get the a/v shared memory handle information
@@ -234,7 +239,7 @@ private:
void getAidlIpAddress(DemuxIpAddress ipAddr,
TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
- int copyData(uint8_t* buffer, int size);
+ int copyData(int8_t* buffer, int size);
void checkIsMediaFilter(DemuxFilterType type);
void handleAvShareMemory();
void closeAvSharedMemory();
@@ -259,7 +264,7 @@ private:
*/
sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
- unique_ptr<MQ> mFilterMQ;
+ AidlMQ* mFilterMQ;
EventFlag* mFilterMQEventFlag;
sp<FilterClientCallback> mCallback;
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 08573a61c623..f454907851a4 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -27,23 +27,43 @@ using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendType;
namespace android {
@@ -160,9 +180,16 @@ vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> stat
vector<FrontendStatus> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatus(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatus(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatus(aidlStatus);
}
if (mFrontend != NULL && statusTypes.size() > 0) {
@@ -180,14 +207,22 @@ vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> stat
return status;
}
+
vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
vector<FrontendStatusTypeExt1_1> statusTypes) {
vector<FrontendStatusExt1_1> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatusExt(aidlStatus);
}
if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
@@ -302,6 +337,400 @@ int FrontendClient::getId() {
return mId;
}
+vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatus> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatus status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::isDemodLocked: {
+ status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::snr: {
+ status.snr(s.get<TunerFrontendStatus::snr>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::ber: {
+ status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::per: {
+ status.per((uint32_t)s.get<TunerFrontendStatus::per>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::preBer: {
+ status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalQuality: {
+ status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalStrength: {
+ status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::symbolRate: {
+ status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::innerFec: {
+ status.innerFec(static_cast<FrontendInnerFec>(
+ s.get<TunerFrontendStatus::innerFec>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::modulation: {
+ auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::inversion: {
+ status.inversion(static_cast<FrontendDvbcSpectralInversion>(
+ s.get<TunerFrontendStatus::inversion>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::lnbVoltage: {
+ status.lnbVoltage(static_cast<LnbVoltage>(
+ s.get<TunerFrontendStatus::lnbVoltage>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpId: {
+ status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isEWBS: {
+ status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::agc: {
+ status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLnaOn: {
+ status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLayerError: {
+ auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
+ hidl_vec<bool> e(aidlE.begin(), aidlE.end());
+ status.isLayerError(e);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::mer: {
+ status.mer(s.get<TunerFrontendStatus::mer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::freqOffset: {
+ status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::hierarchy: {
+ status.hierarchy(static_cast<FrontendDvbtHierarchy>(
+ s.get<TunerFrontendStatus::freqOffset>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isRfLocked: {
+ status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpInfo: {
+ int size = s.get<TunerFrontendStatus::plpInfo>().size();
+ status.plpInfo().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
+ status.plpInfo()[i] = {
+ .plpId = (uint8_t)aidlInfo.plpId,
+ .isLocked = aidlInfo.isLocked,
+ .uec = (uint32_t)aidlInfo.uec,
+ };
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
+vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
+ vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatusExt1_1> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatusExt1_1 status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::modulations: {
+ for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
+ int size = status.modulations().size();
+ status.modulations().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulations()[size].dvbc(
+ static_cast<FrontendDvbcModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulations()[size].dvbs(
+ static_cast<FrontendDvbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBT:
+ status.modulations()[size].dvbt(
+ static_cast<FrontendDvbtConstellation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulations()[size].isdbs(
+ static_cast<FrontendIsdbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulations()[size].isdbs3(
+ static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulations()[size].isdbt(
+ static_cast<FrontendIsdbtModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC:
+ status.modulations()[size].atsc(
+ static_cast<FrontendAtscModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.modulations()[size].atsc3(
+ static_cast<FrontendAtsc3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::DTMB:
+ status.modulations()[size].dtmb(
+ static_cast<FrontendDtmbModulation>(aidlMod));
+ break;
+ default:
+ status.modulations().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bers: {
+ auto aidlB = s.get<TunerFrontendStatus::bers>();
+ hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
+ status.bers(b);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::codeRates: {
+ int size = s.get<TunerFrontendStatus::codeRates>().size();
+ status.codeRates().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
+ status.codeRates()[i] =
+ static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bandwidth: {
+ auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+ switch (mType) {
+ case (int)FrontendType::ATSC3:
+ status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBC:
+ status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBT:
+ status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::interval: {
+ auto aidlInter = s.get<TunerFrontendStatus::interval>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::transmissionMode: {
+ auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.transmissionMode().dvbt(
+ static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.transmissionMode().dtmb(
+ static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::uec: {
+ status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::systemId: {
+ status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::interleaving: {
+ for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
+ int size = status.interleaving().size();
+ status.interleaving().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.interleaving()[size].dvbc(
+ static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.interleaving()[size].atsc3(
+ static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::DTMB:
+ status.interleaving()[size].dtmb(
+ static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ break;
+ default:
+ status.interleaving().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isdbtSegment: {
+ auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
+ hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
+ status.isdbtSegment(s);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::tsDataRate: {
+ auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
+ hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
+ status.tsDataRate(ts);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::rollOff: {
+ auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+ switch (mType) {
+ case (int)FrontendType::DVBS:
+ status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::isMiso: {
+ status.isMiso(s.get<TunerFrontendStatus::isMiso>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLinear: {
+ status.isLinear(s.get<TunerFrontendStatus::isLinear>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isShortFrames: {
+ status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
bool isExtended = validateExtendedSettings(settingsExt1_1);
@@ -686,14 +1115,15 @@ FrontendScanMessage TunerFrontendCallback::getHalScanMessage(
vector<TunerFrontendScanAtsc3PlpInfo> plp =
message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
- for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+ int size = plp.size();
+ plpInfo.resize(size);
+ for (int i = 0; i < size; i++) {
+ auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
FrontendScanAtsc3PlpInfo p{
.plpId = static_cast<uint8_t>(info.plpId),
.bLlsFlag = info.llsFlag,
};
- int size = plpInfo.size();
- plpInfo.resize(size + 1);
- plpInfo[size] = p;
+ plpInfo[i] = p;
}
scanMessage.atsc3PlpInfos(plpInfo);
break;
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index b0107ff174d4..298b3974aeb9 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -43,6 +43,7 @@ using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings;
using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -182,6 +183,9 @@ public:
int getId();
private:
+ vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
+ vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
+
TunerFrontendSettings getAidlFrontendSettings(
const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
TunerFrontendAnalogSettings getAidlAnalogSettings(
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 27ea6e596204..432238d261e5 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <utils/Log.h>
+#include "ClientHelper.h"
#include "TimeFilterClient.h"
using ::android::hardware::tv::tuner::V1_0::Result;
@@ -28,13 +29,12 @@ namespace android {
/////////////// TimeFilterClient ///////////////////////
-// TODO: pending aidl interface
-TimeFilterClient::TimeFilterClient() {
- //mTunerTimeFilter = tunerTimeFilter;
+TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) {
+ mTunerTimeFilter = tunerTimeFilter;
}
TimeFilterClient::~TimeFilterClient() {
- //mTunerTimeFilter = NULL;
+ mTunerTimeFilter = NULL;
mTimeFilter = NULL;
}
@@ -44,7 +44,10 @@ void TimeFilterClient::setHidlTimeFilter(sp<ITimeFilter> timeFilter) {
}
Result TimeFilterClient::setTimeStamp(long timeStamp) {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->setTimeStamp(timeStamp);
@@ -54,7 +57,10 @@ Result TimeFilterClient::setTimeStamp(long timeStamp) {
}
Result TimeFilterClient::clearTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->clearTimeStamp();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->clearTimeStamp();
@@ -64,7 +70,14 @@ Result TimeFilterClient::clearTimeStamp() {
}
long TimeFilterClient::getTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t timeStamp;
+ Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return timeStamp;
+ }
if (mTimeFilter != NULL) {
Result res;
@@ -84,27 +97,37 @@ long TimeFilterClient::getTimeStamp() {
}
long TimeFilterClient::getSourceTime() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t sourceTime;
+ Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return sourceTime;
+ }
if (mTimeFilter != NULL) {
Result res;
- long timestamp;
+ long sourceTime;
mTimeFilter->getSourceTime(
[&](Result r, uint64_t t) {
res = r;
- timestamp = t;
+ sourceTime = t;
});
if (res != Result::SUCCESS) {
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
- return timestamp;
+ return sourceTime;
}
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
Result TimeFilterClient::close() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->close();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->close();
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 9a9d172665ec..56ddd68d363e 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,12 +17,13 @@
#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
-//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
#include <android/hardware/tv/tuner/1.1/types.h>
-//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using Status = ::ndk::ScopedAStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
@@ -36,8 +37,7 @@ namespace android {
struct TimeFilterClient : public RefBase {
public:
- // TODO: add TunerTimeFilter as parameter.
- TimeFilterClient();
+ TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
~TimeFilterClient();
// TODO: remove after migration to Tuner Service is done.
@@ -73,8 +73,7 @@ private:
* An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
* opens an TimeFilter. Default null when time filter is not opened.
*/
- // TODO: pending on aidl interface
- //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+ shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
/**
* A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 14393a1081c6..a604490daf58 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -200,7 +200,14 @@ sp<DemuxClient> TunerClient::openDemux(int demuxHandle) {
}
shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
- // pending aidl interface
+ if (mTunerService != NULL) {
+ TunerDemuxCapabilities aidlCaps;
+ Status s = mTunerService->getDemuxCaps(&aidlCaps);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+ }
if (mTuner != NULL) {
Result res;
@@ -459,6 +466,26 @@ sp<IDescrambler> TunerClient::openHidlDescrambler() {
return descrambler;
}
+DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
+ DemuxCapabilities caps{
+ .numDemux = (uint32_t)aidlCaps.numDemux,
+ .numRecord = (uint32_t)aidlCaps.numRecord,
+ .numPlayback = (uint32_t)aidlCaps.numPlayback,
+ .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
+ .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
+ .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
+ .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
+ .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
+ .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
+ .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
+ .filterCaps = (uint32_t)aidlCaps.filterCaps,
+ .bTimeFilter = aidlCaps.bTimeFilter,
+ };
+ caps.linkCaps.resize(aidlCaps.linkCaps.size());
+ copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
+ return caps;
+}
+
FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ce666196be8..acd018eb1e44 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
using ::aidl::android::media::tv::tuner::ITunerService;
using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -145,6 +146,7 @@ private:
sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
+ DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 45f42f1b5dc6..48d738039696 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -264,7 +264,7 @@ AFont* _Nonnull AFontMatcher_match(
static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
1 /* maxRun */);
- const minikin::Font* font = runs[0].fakedFont.font;
+ const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2716e092c6c3..3b054e942178 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@ public class ConnectivityManager {
}
}
- /** {@hide} - returns the factory serial number */
- @UnsupportedAppUsage
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public int registerNetworkFactory(Messenger messenger, String name) {
- try {
- return mService.registerNetworkFactory(messenger, name);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public void unregisterNetworkFactory(Messenger messenger) {
- try {
- mService.unregisterNetworkFactory(messenger);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* Registers the specified {@link NetworkProvider}.
* Each listener must only be registered once. The listener can be unregistered with
@@ -4871,9 +4845,13 @@ public class ConnectivityManager {
}
}
- private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
- Log.d(TAG, "setOemNetworkPreference called with preference: "
- + preference.toString());
+ private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ try {
+ mService.setOemNetworkPreference(preference);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+ throw e.rethrowFromSystemServer();
+ }
}
@NonNull
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e413943..e2672c480c10 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.UidRange;
import android.net.QosSocketInfo;
@@ -156,9 +157,6 @@ interface IConnectivityManager
boolean requestBandwidthUpdate(in Network network);
- int registerNetworkFactory(in Messenger messenger, in String name);
- void unregisterNetworkFactory(in Messenger messenger);
-
int registerNetworkProvider(in Messenger messenger, in String name);
void unregisterNetworkProvider(in Messenger messenger);
@@ -243,4 +241,6 @@ interface IConnectivityManager
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
void unregisterQosCallback(in IQosCallback callback);
+
+ void setOemNetworkPreference(in OemNetworkPreferences preference);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a61ade6..a6e2af9b1674 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@ import android.provider.settings.validators.GlobalSettingsValidators;
import android.provider.settings.validators.SecureSettingsValidators;
import android.provider.settings.validators.SystemSettingsValidators;
import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
@@ -95,10 +96,11 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final String KEY_NETWORK_POLICIES = "network_policies";
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 8;
+ private static final int STATE_VERSION = 9;
// Versioning of the Network Policies backup payload.
private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@ public class SettingsBackupAgent extends BackupAgentHelper {
// Slots in the checksum array. Never insert new items in the middle
// of this array; new slots must be appended.
- private static final int STATE_SYSTEM = 0;
- private static final int STATE_SECURE = 1;
- private static final int STATE_LOCALE = 2;
- private static final int STATE_WIFI_SUPPLICANT = 3;
- private static final int STATE_WIFI_CONFIG = 4;
- private static final int STATE_GLOBAL = 5;
- private static final int STATE_LOCK_SETTINGS = 6;
- private static final int STATE_SOFTAP_CONFIG = 7;
- private static final int STATE_NETWORK_POLICIES = 8;
- private static final int STATE_WIFI_NEW_CONFIG = 9;
- private static final int STATE_DEVICE_CONFIG = 10;
-
- private static final int STATE_SIZE = 11; // The current number of state items
+ private static final int STATE_SYSTEM = 0;
+ private static final int STATE_SECURE = 1;
+ private static final int STATE_LOCALE = 2;
+ private static final int STATE_WIFI_SUPPLICANT = 3;
+ private static final int STATE_WIFI_CONFIG = 4;
+ private static final int STATE_GLOBAL = 5;
+ private static final int STATE_LOCK_SETTINGS = 6;
+ private static final int STATE_SOFTAP_CONFIG = 7;
+ private static final int STATE_NETWORK_POLICIES = 8;
+ private static final int STATE_WIFI_NEW_CONFIG = 9;
+ private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
+
+ private static final int STATE_SIZE = 12; // The current number of state items
// Number of entries in the checksum array at various version numbers
private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@ public class SettingsBackupAgent extends BackupAgentHelper {
8, // version 5 added STATE_SOFTAP_CONFIG
9, // version 6 added STATE_NETWORK_POLICIES
10, // version 7 added STATE_WIFI_NEW_CONFIG
- STATE_SIZE // version 8 added STATE_DEVICE_CONFIG
+ 11, // version 8 added STATE_DEVICE_CONFIG
+ STATE_SIZE // version 9 added STATE_SIM_SPECIFIC_SETTINGS
};
private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
@@ -218,6 +222,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
byte[] netPoliciesData = getNetworkPolicies();
byte[] wifiFullConfigData = getNewWifiConfigData();
byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+ byte[] simSpecificSettingsData = getSimSpecificSettingsData();
long[] stateChecksums = readOldChecksums(oldState);
@@ -246,6 +251,9 @@ public class SettingsBackupAgent extends BackupAgentHelper {
stateChecksums[STATE_DEVICE_CONFIG] =
writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
deviceSpecificInformation, data);
+ stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+ writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+ KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -386,6 +394,12 @@ public class SettingsBackupAgent extends BackupAgentHelper {
preservedSettings);
break;
+ case KEY_SIM_SPECIFIC_SETTINGS:
+ byte[] restoredSimSpecificSettings = new byte[size];
+ data.readEntityData(restoredSimSpecificSettings, 0, size);
+ restoreSimSpecificSettings(restoredSimSpecificSettings);
+ break;
+
default :
data.skipEntityData();
@@ -1189,6 +1203,20 @@ public class SettingsBackupAgent extends BackupAgentHelper {
return true;
}
+ private byte[] getSimSpecificSettingsData() {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+ Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ + " successfully retrieved");
+
+ return simSpecificData;
+ }
+
+ private void restoreSimSpecificSettings(byte[] data) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ }
+
private void updateWindowManagerIfNeeded(Integer previousDensity) {
int newDensity;
try {
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
new file mode 100644
index 000000000000..cb4686dd04a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners android:radius="@dimen/notification_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
new file mode 100644
index 000000000000..e5389f3b99ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
@@ -0,0 +1,44 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:useLevel="false"
+ android:tint="?android:colorControlNormal">
+ <solid android:color="@*android:color/white_disabled_material" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <rotate
+ android:fromDegrees="270"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:toDegrees="270">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:tint="?android:attr/colorControlActivated">
+ <solid android:color="@android:color/white" />
+ </shape>
+ </rotate>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml
new file mode 100644
index 000000000000..983999f9a5f8
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_in_dialog.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/control_detail_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
+ android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
+ android:padding="8dp"
+ android:orientation="vertical"
+ android:background="@drawable/controls_dialog_bg">
+
+ <com.android.systemui.globalactions.MinHeightScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/global_actions_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ android:clipToPadding="false" />
+
+ </com.android.systemui.globalactions.MinHeightScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 04de9784812f..862076b650b9 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -81,6 +81,17 @@
android:contentDescription="@string/accessibility_phone_button"
android:tint="?attr/wallpaperTextColor" />
+ <ImageView
+ android:id="@+id/alt_left_button"
+ android:layout_height="@dimen/keyguard_affordance_height"
+ android:layout_width="@dimen/keyguard_affordance_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="48dp"
+ android:visibility="gone" />
+
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8f3345f9d85c..e2f3e2a306e3 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -74,7 +74,7 @@
android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginBottom="42dp"
android:layout_marginHorizontal="48dp"
android:adjustViewBounds="true"
app:layout_constrainedHeight="true"
@@ -91,19 +91,33 @@
android:id="@+id/crop_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginBottom="42dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:handleThickness="3dp"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
app:handleColor="@*android:color/accent_device_default"
- app:scrimColor="#9444"
+ app:scrimColor="@color/screenshot_crop_scrim"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
+ <com.android.systemui.screenshot.MagnifierView
+ android:id="@+id/magnifier"
+ android:visibility="invisible"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="@color/screenshot_crop_scrim"
+ app:borderThickness="4dp"
+ app:borderColor="#fff"
+ />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index c0788051efed..6ae306e17209 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,4 +20,17 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="0.5"/>
+ systemui:sensorTouchAreaCoefficient="0.5">
+
+ <!-- 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.UdfpsView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6dff2e3ef6c2..a6321fe14894 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -19,4 +19,7 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6c55fb62e638..8166e35d5b6a 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -178,6 +178,14 @@
<attr name="scrimColor" format="color" />
</declare-styleable>
+ <declare-styleable name="MagnifierView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ <attr name="borderThickness" format="dimension" />
+ <attr name="borderColor" format="color" />
+ </declare-styleable>
+
<declare-styleable name="RoundedCornerProgressDrawable">
<attr name="android:drawable" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7cf3e91dbba..5fb6de7bb588 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -197,6 +197,9 @@
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+ <!-- Long screenshot UI -->
+ <color name="screenshot_crop_scrim">#9444</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>
@@ -261,6 +264,7 @@
<color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
<color name="control_thumbnail_tint">#33000000</color>
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
+ <color name="controls_lockscreen_scrim">#AA000000</color>
<!-- Docked misalignment message -->
<color name="misalignment_text_color">#F28B82</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 44eeba1146a4..bb04c3b35628 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -565,4 +565,7 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d92f4ea65390..afa98b56a13c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -345,6 +345,7 @@
<dimen name="screenshot_action_chip_padding_end">16dp</dimen>
<dimen name="screenshot_action_chip_text_size">14sp</dimen>
<dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+ <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
<!-- The width of the view containing navigation buttons -->
@@ -1116,6 +1117,9 @@
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <!-- UDFPS enrollment progress bar thickness -->
+ <dimen name="udfps_enroll_progress_thickness">12dp</dimen>
+
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
<dimen name="wireless_charging_dots_radius_end">4dp</dimen>
@@ -1259,6 +1263,7 @@
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_offset">100dp</dimen>
+ <dimen name="controls_activity_view_side_offset">12dp</dimen>
<dimen name="controls_activity_view_text_size">17sp</dimen>
<dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4b04eebfddf0..7c72548a7252 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -662,6 +662,19 @@
<item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
</style>
+ <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="android:windowAnimationStyle">@style/Animation.Fade</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ </style>
+
+ <style name="Animation.Fade">
+ <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+ </style>
+
<style name="Control" />
<style name="Control.MenuItem">
@@ -752,4 +765,12 @@
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
+
+ <style name="UdfpsProgressBarStyle"
+ parent="android:style/Widget.Material.ProgressBar.Horizontal">
+ <item name="android:indeterminate">false</item>
+ <item name="android:max">10000</item>
+ <item name="android:mirrorForRtl">false</item>
+ <item name="android:progressDrawable">@drawable/udfps_progress_bar</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 3584c82bc57d..e2ca349cc5c8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -72,9 +72,13 @@ public abstract class ActivityOptionsCompat {
return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
}
+ /**
+ * Returns ActivityOptions for overriding task transition animation.
+ */
public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
int exitResId, final Runnable callback, final Handler callbackHandler) {
- return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+ return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+ callbackHandler,
new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1f084e..e6477f158c27 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@ package com.android.systemui.shared.system;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
import android.os.RemoteException;
import android.util.Log;
@@ -65,13 +67,17 @@ public class RemoteAnimationAdapterCompat {
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ RemoteAnimationTargetCompat.wrap(nonApps);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -83,8 +89,8 @@ public class RemoteAnimationAdapterCompat {
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
- animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+ nonAppsCompat, animationFinishedCallback);
}
@Override
@@ -104,6 +110,9 @@ public class RemoteAnimationAdapterCompat {
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ // TODO(bc-unlock): Build wrapped object for non-apps target.
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ new RemoteAnimationTargetCompat[0];
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -147,7 +156,10 @@ public class RemoteAnimationAdapterCompat {
}
}
t.apply();
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ // TODO(bc-unlcok): Pass correct transit type.
+ remoteAnimationAdapter.onAnimationStart(
+ TRANSIT_OLD_NONE,
+ appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6bd0b9..007629254c7c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
package com.android.systemui.shared.system;
+import android.view.WindowManager;
+
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+ void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
void onAnimationCancelled();
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca05d428..276036c400e1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@ public class KeyguardDisplayManager {
@Override
public void onDisplayChanged(int displayId) {
- if (displayId == DEFAULT_DISPLAY) return;
- final Presentation presentation = mPresentations.get(displayId);
- if (presentation != null && mShowing) {
- hidePresentation(displayId);
- // update DisplayInfo.
- final Display display = mDisplayService.getDisplay(displayId);
- if (display != null) {
- showPresentation(display);
- }
- }
+
}
@Override
@@ -305,15 +296,16 @@ public class KeyguardDisplayManager {
}
@Override
+ public void onDisplayChanged() {
+ updateBounds();
+ getWindow().getDecorView().requestLayout();
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ updateBounds();
setContentView(LayoutInflater.from(mContext)
.inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@ public class KeyguardDisplayManager {
mKeyguardClockSwitchController.init();
}
+
+ private void updateBounds() {
+ final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+ .getBounds();
+ mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+ mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index e07c84034b31..5290986b2a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -38,6 +38,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
private static final String TAG = "UdfpsAnimationEnroll";
private static final float SHADOW_RADIUS = 5.f;
+ private static final float PROGRESS_BAR_RADIUS = 140.f;
@Nullable private RectF mSensorRect;
@NonNull private final Paint mSensorPaint;
@@ -81,12 +82,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
@Override
public int getPaddingX() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
public int getPaddingY() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
@@ -104,12 +105,4 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation {
public int getOpacity() {
return 0;
}
-
- public void onEnrollmentProgress(int remaining) {
- Log.d(TAG, "Remaining: " + remaining);
- }
-
- public void onEnrollmentHelp() {
- Log.d(TAG, "onEnrollmentHelp");
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 4e3419e1fab3..41ea4d66f575 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -103,18 +103,6 @@ public class UdfpsAnimationView extends View implements DozeReceiver,
postInvalidate();
}
- void onEnrollmentProgress(int remaining) {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
- }
- }
-
- void onEnrollmentHelp() {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
- }
- }
-
public int getPaddingX() {
if (mUdfpsAnimation == null) {
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index baa597398188..edf046864a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -84,6 +84,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
private boolean mIsOverlayRequested;
// Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
private int mRequestReason;
+ @Nullable UdfpsEnrollHelper mEnrollHelper;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -95,6 +96,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay(int sensorId, int reason) {
+ if (reason == IUdfpsOverlayController.REASON_ENROLL) {
+ mEnrollHelper = new UdfpsEnrollHelper();
+ }
UdfpsController.this.showOverlay(reason);
}
@@ -260,15 +264,17 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
// Transform dimensions if the device is in landscape mode.
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
- mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y =
- p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
case Surface.ROTATION_270:
- mCoreLayoutParams.x =
- p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
default:
@@ -295,6 +301,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
Log.v(TAG, "showUdfpsOverlay | adding window");
final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
mView.setUdfpsAnimation(animation);
+ mView.setEnrollHelper(mEnrollHelper);
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
mIsOverlayShowing = true;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
new file mode 100644
index 000000000000..ac6a2121eaae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -0,0 +1,48 @@
+/*
+ * 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 androidx.annotation.NonNull;
+
+/**
+ * Helps keep track of enrollment state and animates the progress bar accordingly.
+ */
+public class UdfpsEnrollHelper {
+ private static final String TAG = "UdfpsEnrollHelper";
+
+
+ private int mTotalSteps = -1;
+ private int mCurrentProgress = 0;
+
+ void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+ if (mTotalSteps == -1) {
+ mTotalSteps = remaining;
+ }
+
+ mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
+ / (mTotalSteps + 1);
+ progressBar.setProgress(mCurrentProgress, true /* animate */);
+ }
+
+ void updateProgress(@NonNull UdfpsProgressBar progressBar) {
+ progressBar.setProgress(mCurrentProgress);
+ }
+
+ void onEnrollmentHelp() {
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
new file mode 100644
index 000000000000..84e2fab7bf6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.widget.ProgressBar;
+
+import com.android.systemui.R;
+
+/**
+ * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting
+ * from the 12 o'clock position. This view maintain equal width and height using a strategy similar
+ * to "centerInside" for ImageView.
+ */
+public class UdfpsProgressBar extends ProgressBar {
+
+ public UdfpsProgressBar(Context context) {
+ this(context, null);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int measuredHeight = getMeasuredHeight();
+ final int measuredWidth = getMeasuredWidth();
+
+ final int length = Math.min(measuredHeight, measuredWidth);
+ setMeasuredDimension(length, length);
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 7e378d3c568e..b21e1b5ebb15 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -56,6 +56,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
+ @Nullable private UdfpsProgressBar mProgressBar;
+
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,6 +66,7 @@ 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);
@@ -110,6 +113,18 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
mAnimationView.setAnimation(animation);
+ if (animation instanceof UdfpsAnimationEnroll) {
+ mProgressBar.setVisibility(View.VISIBLE);
+ } else {
+ mProgressBar.setVisibility(View.GONE);
+ }
+ }
+
+ void setEnrollHelper(@Nullable UdfpsEnrollHelper enrollHelper) {
+ mEnrollHelper = enrollHelper;
+ if (mEnrollHelper != null) {
+ mEnrollHelper.updateProgress(mProgressBar);
+ }
}
@Override
@@ -138,6 +153,11 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
}
@Override
+ protected void onFinishInflate() {
+ mProgressBar = findViewById(R.id.progress_bar);
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mSensorRect.set(0 + mAnimationView.getPaddingX(),
@@ -233,10 +253,10 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
}
void onEnrollmentProgress(int remaining) {
- mAnimationView.onEnrollmentProgress(remaining);
+ mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
}
void onEnrollmentHelp() {
- mAnimationView.onEnrollmentHelp();
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
new file mode 100644
index 000000000000..8e878cf76ad9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.controls.ui
+
+import android.app.Dialog
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+
+/**
+ * Show the controls space inside a dialog, as from the lock screen.
+ */
+class ControlsDialog(
+ thisContext: Context,
+ val broadcastDispatcher: BroadcastDispatcher
+) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
+
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ dismiss()
+ }
+ }
+ }
+
+ init {
+ window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+
+ setContentView(R.layout.controls_in_dialog)
+
+ requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+ setOnClickListener { dismiss() }
+ (getParent() as View).setOnClickListener { dismiss() }
+ }
+ }
+
+ fun show(
+ controller: ControlsUiController
+ ): ControlsDialog {
+ super.show()
+
+ val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
+ vg.alpha = 0f
+ controller.show(vg, { /* do nothing */ })
+
+ vg.animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setDuration(300)
+
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ broadcastDispatcher.registerReceiver(receiver, filter)
+
+ return this
+ }
+
+ override fun dismiss() {
+ broadcastDispatcher.unregisterReceiver(receiver)
+
+ if (!isShowing()) return
+
+ super.dismiss()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e91b76b..17f7ccf0d967 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+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_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import android.app.ActivityTaskManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
@@ -26,8 +32,17 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@ public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ *
+ * Note: Must be consistent with WindowManagerService.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ private static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -52,6 +82,21 @@ public class KeyguardService extends Service {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+ if (sEnableRemoteKeyguardAnimation) {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter exitAnimationAdapter =
+ new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ exitAnimationAdapter);
+ final RemoteAnimationAdapter occludeAnimationAdapter =
+ new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
+ }
}
@Override
@@ -76,6 +121,48 @@ public class KeyguardService extends Service {
}
}
+ private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+ checkPermission();
+ mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+ null /* nonApps */, finishedCallback);
+ Trace.endSection();
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
+ private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+ // run animation.
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
@@ -225,6 +312,11 @@ public class KeyguardService extends Service {
mKeyguardViewMediator.onBootCompleted();
}
+ /**
+ * @deprecated When remote animation is enabled, this won't be called anymore. Use
+ * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+ */
+ @Deprecated
@Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e7326698e43e..5a918d4808d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -65,8 +68,13 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -85,6 +93,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -1318,6 +1327,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
// SHOW_WHEN_LOCKED activity instead.
+ // TODO(bc-unlock): Migrate to remote animation.
startKeyguardExitAnimation(0, 0);
}
@@ -1703,7 +1713,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
- handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+ handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+ params.mApps, params.mWallpapers, params.mNonApps,
+ params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
Trace.endSection();
break;
@@ -1990,15 +2002,19 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
+ // TODO(bc-unlock): Fill parameters
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
- mHideAnimation.getDuration());
+ mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
+ null /* nonApps */, null /* finishedCallback */);
}
}
Trace.endSection();
}
- private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2047,49 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+ // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+ // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+ // supported, so it's always null.
+ mContext.getMainExecutor().execute(() -> {
+ if (finishedCallback == null) {
+ return;
+ }
+
+ // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+ final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+ final RemoteAnimationTarget primary = apps[0];
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(400 /* duration */);
+ anim.setInterpolator(Interpolators.LINEAR);
+ anim.addUpdateListener((ValueAnimator animation) -> {
+ SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction())
+ .build();
+ applier.scheduleApply(params);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+ });
+ anim.start();
+ });
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -2223,10 +2282,55 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
return mKeyguardViewControllerLazy.get();
}
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+ * animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @deprecated Will be migrate to remote animation soon.
+ */
+ @Deprecated
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and start running keyguard exit animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
- new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+ new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+ wallpapers, nonApps, finishedCallback));
mHandler.sendMessage(msg);
Trace.endSection();
}
@@ -2300,12 +2404,26 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
private static class StartKeyguardExitAnimParams {
+ @WindowManager.TransitionOldType int mTransit;
long startTime;
long fadeoutDuration;
-
- private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+ RemoteAnimationTarget[] mApps;
+ RemoteAnimationTarget[] mWallpapers;
+ RemoteAnimationTarget[] mNonApps;
+ IRemoteAnimationFinishedCallback mFinishedCallback;
+
+ private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ this.mTransit = transit;
this.startTime = startTime;
this.fadeoutDuration = fadeoutDuration;
+ this.mApps = apps;
+ this.mWallpapers = wallpapers;
+ this.mNonApps = nonApps;
+ this.mFinishedCallback = finishedCallback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e91896be9e..580cbcf8ce47 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@ import android.app.INotificationManager;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -36,12 +34,9 @@ import android.provider.Settings;
import android.util.Log;
import android.view.ViewGroup;
-import androidx.preference.PreferenceManager;
-
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.util.List;
@@ -128,26 +123,17 @@ public class PeopleSpaceActivity extends Activity {
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
if (PeopleSpaceUtils.DEBUG) {
Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ tile.getId() + " for widget ID: "
+ mAppWidgetId);
}
- // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
- editor.putString(String.valueOf(mAppWidgetId), tile.getId());
- editor.putInt(tile.getId(), mAppWidgetId);
- editor.apply();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
- appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
- int[] widgetIds = appWidgetManager.getAppWidgetIds(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+ PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+ int[] widgetIds = new int[mAppWidgetId];
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
- mNotificationManager);
+ mPeopleManager);
finishActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968f7546..994dc6d78507 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@ package com.android.systemui.people;
import static android.app.Notification.EXTRA_MESSAGES;
+import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -70,11 +71,13 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -89,6 +92,13 @@ public class PeopleSpaceUtils {
private static final int MIN_HOUR = 1;
private static final int ONE_DAY = 1;
public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+ public static final String PACKAGE_NAME = "package_name";
+ public static final String USER_ID = "user_id";
+ public static final String SHORTCUT_ID = "shortcut_id";
+
+ public static final String EMPTY_STRING = "";
+ public static final int INVALID_WIDGET_ID = -1;
+ public static final int INVALID_USER_ID = -1;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -171,70 +181,174 @@ public class PeopleSpaceUtils {
* notification being posted or removed.
*/
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
- IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE));
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
- try {
- List<PeopleSpaceTile> tiles =
- PeopleSpaceUtils.getTiles(context, notificationManager,
- peopleManager, launcherApps);
- for (int appWidgetId : appWidgetIds) {
- String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
- if (DEBUG) {
- Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
- }
+ for (int appWidgetId : appWidgetIds) {
+ PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
+ if (tile == null) {
+ if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+ //TODO: Delete app widget id when crash is fixed (b/172932636)
+ continue;
+ }
- Optional<PeopleSpaceTile> entry = tiles.stream().filter(
- e -> e.getId().equals(shortcutId)).findFirst();
+ if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId);
- if (!entry.isPresent() || shortcutId == null) {
- if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
- //TODO: Delete app widget id when crash is fixed (b/175486868)
- continue;
- }
- // Augment current tile based on stored fields.
- PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
- appWidgetId);
+ // Tell the AppWidgetManager to perform an update on the current app widget.
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+
+ widgetIdToTile.put(appWidgetId, tile);
+ }
+ getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
+ }
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+ @Nullable
+ private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+ AppWidgetManager appWidgetManager,
+ Context context, int appWidgetId) {
+ try {
+ // Migrate storage for existing users.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+ if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+ if (shortcutId == null) {
+ Log.e(TAG, "Cannot restore widget");
+ return null;
+ }
+ migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+ pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ }
- // Tell the AppWidgetManager to perform an update on the current app widget.
- appWidgetManager.updateAppWidget(appWidgetId, views);
+ // Check if tile is cached.
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ if (tile != null) {
+ return tile;
+ }
- widgetIdToTile.put(appWidgetId, tile);
+ // If tile is null, we need to retrieve from persisted storage.
+ if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+ if (channel == null) {
+ Log.d(TAG, "Could not retrieve conversation from storage");
+ return null;
}
+ return new PeopleSpaceTile.Builder(channel, launcherApps).build();
} catch (Exception e) {
- Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+ Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return null;
}
- getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
- /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
- @VisibleForTesting
- static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
- AppWidgetManager appWidgetManager, int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
- if (storedTile == null) {
- return tile;
+ /** Best-effort attempts to migrate existing users to the new storage format. */
+ // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+ private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+ int appWidgetId) {
+ try {
+ List<PeopleSpaceTile> tiles =
+ PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+ IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)),
+ context.getSystemService(LauncherApps.class));
+ Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+ e -> e.getId().equals(shortcutId)).findFirst();
+ if (entry.isPresent()) {
+ if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+ setStorageForTile(context, entry.get(), appWidgetId);
+ } else {
+ Log.e(TAG, "Could not migrate user");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not query conversations");
}
- return tile.toBuilder()
- .setBirthdayText(storedTile.getBirthdayText())
- .setNotificationKey(storedTile.getNotificationKey())
- .setNotificationContent(storedTile.getNotificationContent())
- .setNotificationDataUri(storedTile.getNotificationDataUri())
- .build();
}
- /** If incoming notification changed tile, store the changes in the tile options. */
- public static void storeNotificationChange(StatusBarNotification sbn,
+ /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+ public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+ // Write relevant persisted storage.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+ widgetEditor.apply();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(appWidgetId), tile.getId());
+ String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+ // Don't overwrite existing widgets with the same key.
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(appWidgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+
+ // Write cached storage.
+ updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+ tile);
+ }
+
+ /** Removes stored data when tile is deleted. */
+ public static void removeStorageForTile(Context context, int widgetId) {
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ String packageName = widgetSp.getString(PACKAGE_NAME, null);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ int userId = widgetSp.getInt(USER_ID, -1);
+
+ // Delete widgetId mapping to key.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.remove(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.remove(String.valueOf(widgetId));
+ editor.apply();
+
+ // Delete all data specifically mapped to widgetId.
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.remove(PACKAGE_NAME);
+ widgetEditor.remove(USER_ID);
+ widgetEditor.remove(SHORTCUT_ID);
+ widgetEditor.apply();
+ }
+
+ /**
+ * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+ * requested update data.
+ */
+ public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+ String packageName, int userId) {
+ SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+ String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+ return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+ && storedUserId == userId;
+ }
+
+ /**
+ * If incoming notification changed tile, store the changes in the tile options.
+ */
+ public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+ Context context,
+ StatusBarNotification sbn,
NotificationAction notificationAction, AppWidgetManager appWidgetManager,
int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
@@ -263,7 +377,7 @@ public class PeopleSpaceUtils {
.setNotificationDataUri(null)
.build();
}
- updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+ updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +791,23 @@ public class PeopleSpaceUtils {
}
return lookupKeysWithBirthdaysToday;
}
+
+ /**
+ * Returns the uniquely identifying key for the conversation.
+ *
+ * <p>{@code userId} will always be a number, so we put user ID as the
+ * delimiter between the app-provided strings of shortcut ID and package name.
+ *
+ * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+ * a {@code packageName} to always start with a letter. This restriction means we are
+ * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+ * case is impossible given the package name restrictions:
+ * <ul>
+ * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+ * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+ * </ul>
+ */
+ public static String getKey(String shortcutId, String packageName, int userId) {
+ return shortcutId + "/" + userId + "/" + packageName;
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index ad6046ba489d..bee54e4dca0a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +29,7 @@ import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@ import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -51,7 +53,7 @@ public class PeopleSpaceWidgetManager {
private final Context mContext;
private IAppWidgetService mAppWidgetService;
private AppWidgetManager mAppWidgetManager;
- private INotificationManager mNotificationManager;
+ private IPeopleManager mPeopleManager;
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@ public class PeopleSpaceWidgetManager {
mContext = context;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mPeopleManager = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
}
- /** Constructor used for testing. */
+ /**
+ * Constructor used for testing.
+ */
@VisibleForTesting
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@ public class PeopleSpaceWidgetManager {
ServiceManager.getService(Context.APPWIDGET_SERVICE));
}
- /** AppWidgetManager setter used for testing. */
+ /**
+ * AppWidgetManager setter used for testing.
+ */
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
mAppWidgetService = appWidgetService;
mAppWidgetManager = appWidgetManager;
- mNotificationManager = notificationManager;
+ mPeopleManager = peopleManager;
}
- /** Updates People Space widgets. */
+ /**
+ * Updates People Space widgets.
+ */
public void updateWidgets() {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@ public class PeopleSpaceWidgetManager {
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mNotificationManager);
+ mAppWidgetManager, mPeopleManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,38 @@ public class PeopleSpaceWidgetManager {
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
- public void storeNotificationChange(StatusBarNotification sbn,
+ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+ RemoteViews views = new RemoteViews(
+ mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+ if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
return;
}
try {
+ String sbnShortcutId = sbn.getShortcutId();
+ if (sbnShortcutId == null) {
+ return;
+ }
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
return;
}
-
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- for (int widgetId : widgetIds) {
- String shortcutId = sp.getString(String.valueOf(widgetId), null);
- if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
- continue;
- }
+ int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+ String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ if (storedWidgetIds.isEmpty()) {
+ return;
+ }
+ for (String widgetIdString : storedWidgetIds) {
+ int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- PeopleSpaceUtils.storeNotificationChange(
+ PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
sbn, notificationAction, mAppWidgetManager, widgetId);
}
} catch (Exception e) {
@@ -159,8 +175,7 @@ public class PeopleSpaceWidgetManager {
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -169,8 +184,7 @@ public class PeopleSpaceWidgetManager {
NotificationListenerService.RankingMap rankingMap
) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -179,8 +193,7 @@ public class PeopleSpaceWidgetManager {
NotificationListenerService.RankingMap rankingMap,
int reason) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -207,4 +220,4 @@ public class PeopleSpaceWidgetManager {
}
};
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc68c54..f5577d30c75c 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
@@ -53,8 +53,8 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
- appWidgetManager, INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+ appWidgetManager, IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 9037192e2ddc..543874325254 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -187,7 +187,7 @@ public class ScreenMediaRecorder {
* @param refreshRate Desired refresh rate
* @return array with supported width, height, and refresh rate
*/
- private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) {
+ private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
double maxScale = 0;
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
@@ -207,25 +207,33 @@ public class ScreenMediaRecorder {
int width = vc.getSupportedWidths().getUpper();
int height = vc.getSupportedHeights().getUpper();
- if (width >= screenWidth && height >= screenHeight
- && vc.isSizeSupported(screenWidth, screenHeight)) {
+ int screenWidthAligned = screenWidth;
+ if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+ screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+ }
+ int screenHeightAligned = screenHeight;
+ if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+ screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+ }
+ if (width >= screenWidthAligned && height >= screenHeightAligned
+ && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
// Desired size is supported, now get the rate
- int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight)
- .getUpper().intValue();
+ int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+ screenHeightAligned).getUpper().intValue();
if (maxRate < refreshRate) {
refreshRate = maxRate;
}
Log.d(TAG, "Screen size supported at rate " + refreshRate);
- return new int[]{screenWidth, screenHeight, refreshRate};
+ return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
}
// Otherwise, continue searching
double scale = Math.min(((double) width / screenWidth),
((double) height / screenHeight));
if (scale > maxScale) {
- maxScale = scale;
+ maxScale = Math.min(1, scale);
maxInfo = vc;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 8e182b415488..c8afd0b6cfe9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -35,7 +35,7 @@ import com.android.systemui.R;
* cropped out.
*/
public class CropView extends View {
- private enum CropBoundary {
+ public enum CropBoundary {
NONE, TOP, BOTTOM
}
@@ -48,8 +48,14 @@ public class CropView extends View {
private float mTopCrop = 0f;
private float mBottomCrop = 1f;
+ // When the user is dragging a handle, these variables store the distance between the top/bottom
+ // crop values and
+ private float mTopDelta = 0f;
+ private float mBottomDelta = 0f;
+
private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
- private float mLastY;
+ private float mStartingY; // y coordinate of ACTION_DOWN
+ private CropInteractionListener mCropInteractionListener;
public CropView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
@@ -73,54 +79,84 @@ public class CropView extends View {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, mTopCrop);
- drawShade(canvas, mBottomCrop, 1f);
- drawHandle(canvas, mTopCrop);
- drawHandle(canvas, mBottomCrop);
+ float top = mTopCrop + mTopDelta;
+ float bottom = mBottomCrop + mBottomDelta;
+ drawShade(canvas, 0, top);
+ drawShade(canvas, bottom, 1f);
+ drawHandle(canvas, top);
+ drawHandle(canvas, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int topPx = fractionToPixels(mTopCrop);
int bottomPx = fractionToPixels(mBottomCrop);
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
- if (mCurrentDraggingBoundary != CropBoundary.NONE) {
- mLastY = event.getY();
- }
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_MOVE
- && mCurrentDraggingBoundary != CropBoundary.NONE) {
- float delta = event.getY() - mLastY;
- if (mCurrentDraggingBoundary == CropBoundary.TOP) {
- mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
- bottomPx - 2 * mCropTouchMargin));
- } else { // Bottom
- mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
- topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
- }
- mLastY = event.getY();
- invalidate();
- return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mStartingY = event.getY();
+ updateListener(event);
+ }
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mStartingY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx,
+ bottomPx - 2 * mCropTouchMargin - topPx));
+ } else { // Bottom
+ mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
+ topPx + 2 * mCropTouchMargin - bottomPx,
+ getMeasuredHeight() - bottomPx));
+ }
+ updateListener(event);
+ invalidate();
+ return true;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ // Commit the delta to the stored crop values.
+ mTopCrop += mTopDelta;
+ mBottomCrop += mBottomDelta;
+ mTopDelta = 0;
+ mBottomDelta = 0;
+ updateListener(event);
+ }
}
return super.onTouchEvent(event);
}
/**
- * @return value [0,1] representing the position of the top crop boundary.
+ * @return value [0,1] representing the position of the top crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getTopBoundary() {
return mTopCrop;
}
/**
- * @return value [0,1] representing the position of the bottom crop boundary.
+ * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getBottomBoundary() {
return mBottomCrop;
}
+ public void setCropInteractionListener(CropInteractionListener listener) {
+ mCropInteractionListener = listener;
+ }
+
+ private void updateListener(MotionEvent event) {
+ if (mCropInteractionListener != null) {
+ float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
+ ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta;
+ mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary,
+ boundaryPosition, fractionToPixels(boundaryPosition));
+ }
+ }
+
private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
fractionToPixels(fracEnd), mShadePaint);
@@ -148,4 +184,17 @@ public class CropView extends View {
}
return CropBoundary.NONE;
}
+
+ /**
+ * Listen for crop motion events and state.
+ */
+ public interface CropInteractionListener {
+ /**
+ * Called whenever CropView has a MotionEvent that can impact the position of the crop
+ * boundaries.
+ */
+ void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
+ int boundaryPositionPx);
+
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
new file mode 100644
index 000000000000..f88715164bc7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+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.
+ *
+ * Not meant to be a general-purpose magnifier!
+ */
+public class MagnifierView extends View implements CropView.CropInteractionListener {
+ private Drawable mDrawable;
+
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ private Path mOuterCircle;
+ private Path mInnerCircle;
+
+ private Path mCheckerboard;
+ private Paint mCheckerboardPaint;
+ private final float mBorderPx;
+ private final int mBorderColor;
+ private float mCheckerboardBoxSize = 40;
+
+ private float mLastCropPosition;
+ private CropView.CropBoundary mCropBoundary;
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.MagnifierView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20));
+ mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0);
+ mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE);
+ t.recycle();
+ mCheckerboardPaint = new Paint();
+ mCheckerboardPaint.setColor(Color.GRAY);
+ }
+
+ public void setImageTileset(ImageTileSet tiles) {
+ if (tiles != null) {
+ mDrawable = tiles.getDrawable();
+ mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight());
+ } else {
+ mDrawable = null;
+ }
+ invalidate();
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int radius = getWidth() / 2;
+ mOuterCircle = new Path();
+ mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW);
+ mInnerCircle = new Path();
+ mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW);
+ mCheckerboard = generateCheckerboard();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // TODO: just draw a circle at the end instead of clipping like this?
+ canvas.clipPath(mOuterCircle);
+ canvas.drawColor(mBorderColor);
+ canvas.clipPath(mInnerCircle);
+
+ // Draw a checkerboard pattern for out of bounds.
+ canvas.drawPath(mCheckerboard, mCheckerboardPaint);
+
+ if (mDrawable != null) {
+ canvas.save();
+ // Translate such that the center of this view represents the center of the crop
+ // boundary.
+ canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2,
+ -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2);
+ mDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2);
+ if (mCropBoundary == CropView.CropBoundary.BOTTOM) {
+ scrimRect.offset(0, getHeight() / 2);
+ }
+ canvas.drawRect(scrimRect, mShadePaint);
+
+ canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint);
+ }
+
+ @Override
+ public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
+ float cropPosition, int cropPositionPx) {
+ mCropBoundary = boundary;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ setPivotX(getWidth() / 2);
+ setPivotY(getHeight() / 2);
+ setScaleX(0.2f);
+ setScaleY(0.2f);
+ setAlpha(0f);
+ setTranslationX((getParentWidth() - getWidth()) / 2);
+ setVisibility(View.VISIBLE);
+ animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f)
+ .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start();
+ break;
+ }
+ }
+
+ private Path generateCheckerboard() {
+ Path path = new Path();
+ int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize);
+ int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize);
+
+ for (int row = 0; row < checkerHeight; row++) {
+ // Alternate starting on the first and second column;
+ int colStart = (row % 2 == 0) ? 0 : 1;
+ for (int col = colStart; col < checkerWidth; col += 2) {
+ path.addRect(col * mCheckerboardBoxSize,
+ row * mCheckerboardBoxSize,
+ (col + 1) * mCheckerboardBoxSize,
+ (row + 1) * mCheckerboardBoxSize,
+ Path.Direction.CW);
+ }
+ }
+ return path;
+ }
+
+ private int getParentWidth() {
+ return ((View) getParent()).getWidth();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 18c379a4650f..25438a6f57ba 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -81,6 +81,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
private View mEdit;
private View mShare;
private CropView mCropView;
+ private MagnifierView mMagnifierView;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -120,13 +121,14 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
mEdit = findViewById(R.id.edit);
mShare = findViewById(R.id.share);
mCropView = findViewById(R.id.crop_view);
+ mMagnifierView = findViewById(R.id.magnifier);
+ mCropView.setCropInteractionListener(mMagnifierView);
mSave.setOnClickListener(this::onClicked);
mCancel.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- //mPreview.setImageDrawable(mImageTileSet.getDrawable());
mConnection.start(this::startCapture);
}
@@ -164,6 +166,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
private void doFinish() {
mPreview.setImageDrawable(null);
+ mMagnifierView.setImageTileset(null);
mImageTileSet.clear();
mCallback.onFinish();
mWindow.getDecorView().getViewTreeObserver()
@@ -273,6 +276,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
session.end(mCallback::onFinish);
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
+ mMagnifierView.setImageTileset(mImageTileSet);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6cbddfd..d08f9736adf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@ import android.view.RemoteAnimationTarget;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
+import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@ public class ActivityLaunchAnimator {
}
@Override
- public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] remoteAnimationTargets,
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+ RemoteAnimationTarget[] remoteAnimationNonAppTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e6efba7ca28b..5d2203b57991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -286,8 +286,7 @@ public class StackScrollAlgorithm {
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
- false /* reverse */);
+ currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
}
}
@@ -301,10 +300,6 @@ public class StackScrollAlgorithm {
* @param currentYPosition The Y position of the current pass of the algorithm. For a forward
* pass, this should be the top of the child; for a reverse pass, the
* bottom of the child.
- * @param reverse Whether we're laying out children in the reverse direction (Y
- * positions
- * decreasing) instead of the forward direction (Y positions
- * increasing).
* @return The Y position after laying out the child. This will be the {@code currentYPosition}
* for the next call to this method, after adjusting for any gaps between children.
*/
@@ -312,8 +307,7 @@ public class StackScrollAlgorithm {
int i,
StackScrollAlgorithmState algorithmState,
AmbientState ambientState,
- float currentYPosition,
- boolean reverse) {
+ float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
final boolean applyGapHeight =
@@ -323,20 +317,12 @@ public class StackScrollAlgorithm {
ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- if (applyGapHeight && !reverse) {
+ if (applyGapHeight) {
currentYPosition += mGapHeight;
}
-
int childHeight = getMaxAllowedChildHeight(child);
- if (reverse) {
- childViewState.yTranslation = currentYPosition
- - (childHeight + mPaddingBetweenElements);
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
- } else {
- childViewState.yTranslation = currentYPosition;
- }
+ childViewState.yTranslation = currentYPosition;
+
boolean isFooterView = child instanceof FooterView;
boolean isEmptyShadeView = child instanceof EmptyShadeView;
@@ -362,16 +348,9 @@ public class StackScrollAlgorithm {
clampPositionToShelf(child, childViewState, ambientState);
}
- if (reverse) {
- currentYPosition = childViewState.yTranslation;
- if (applyGapHeight) {
- currentYPosition -= mGapHeight;
- }
- } else {
- currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
+ currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+ if (currentYPosition <= 0) {
+ childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
}
if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
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 0a366c9bb380..dd1419f4ff42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -48,6 +48,7 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.service.media.CameraPrewarmService;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -60,6 +61,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,10 @@ 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;
@@ -117,11 +123,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+ // TODO(b/179494051): May no longer be needed
private final boolean mShowLeftAffordance;
private final boolean mShowCameraAffordance;
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
+ private ImageView mAltLeftButton;
private ViewGroup mIndicationArea;
private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
@@ -171,6 +179,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
+ private ControlsDialog mControlsDialog;
+ private ControlsComponent mControlsComponent;
+ private int mLockScreenMode;
+ private BroadcastDispatcher mBroadcastDispatcher;
+
public KeyguardBottomAreaView(Context context) {
this(context, null);
}
@@ -236,6 +249,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);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
mEnterpriseDisclosure = findViewById(
R.id.keyguard_indication_enterprise_disclosure);
@@ -334,6 +348,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
+
+ lp = mAltLeftButton.getLayoutParams();
+ lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
+ lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
+ mAltLeftButton.setLayoutParams(lp);
}
private void updateRightAffordanceIcon() {
@@ -392,10 +411,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftAffordanceIcon() {
+ if (mDozing) {
+ mAltLeftButton.setVisibility(GONE);
+ } else if (mAltLeftButton.getDrawable() != null) {
+ mAltLeftButton.setVisibility(VISIBLE);
+ }
+
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
}
+
IconState state = mLeftButton.getIcon();
mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
@@ -669,6 +695,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public void startFinishDozeAnimation() {
long delay = 0;
+ if (mAltLeftButton.getVisibility() == View.VISIBLE) {
+ startFinishDozeAnimationElement(mAltLeftButton, delay);
+ }
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
@@ -744,6 +773,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
+ if (mControlsDialog != null) {
+ mControlsDialog.dismiss();
+ mControlsDialog = null;
+ }
} else {
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
@@ -773,6 +806,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha);
+ mAltLeftButton.setAlpha(alpha);
}
private class DefaultLeftButton implements IntentButton {
@@ -844,4 +878,54 @@ 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() {
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ return;
+ }
+
+ if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
+ return;
+ }
+
+ if (mControlsComponent.getControlsListingController().isPresent()) {
+ mControlsComponent.getControlsListingController().get()
+ .addCallback(list -> {
+ if (!list.isEmpty()) {
+ mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
+ mAltLeftButton.setVisibility(View.VISIBLE);
+ mAltLeftButton.setOnClickListener((v) -> {
+ ControlsUiController ui = mControlsComponent
+ .getControlsUiController().get();
+ mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
+ .show(ui);
+ });
+
+ } else {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ }
+ });
+ }
+ }
+
+ /**
+ * Optionally add controls when in the new lockscreen mode
+ */
+ public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
+ setupControls();
+ }
}
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 a30f193c50a3..e0ef3b6483a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -80,8 +80,10 @@ 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;
@@ -95,6 +97,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -228,6 +231,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+ mKeyguardBottomArea.onLockScreenModeChanged(mode);
}
@Override
@@ -292,7 +296,10 @@ public class NotificationPanelViewController extends PanelViewController {
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
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
private final int mMaxKeyguardNotifications;
@@ -501,6 +508,7 @@ 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
@@ -554,7 +562,10 @@ public class NotificationPanelViewController extends PanelViewController {
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
MediaDataManager mediaDataManager,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ FeatureFlags featureFlags,
+ ControlsComponent controlsComponent,
+ BroadcastDispatcher broadcastDispatcher) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -571,6 +582,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
+ mFeatureFlags = featureFlags;
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -587,6 +599,7 @@ public class NotificationPanelViewController extends PanelViewController {
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
mMediaDataManager = mediaDataManager;
+ mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -625,6 +638,7 @@ public class NotificationPanelViewController extends PanelViewController {
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
+ mBroadcastDispatcher = broadcastDispatcher;
mView.setBackgroundColor(Color.TRANSPARENT);
OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -768,6 +782,26 @@ public class NotificationPanelViewController extends PanelViewController {
lp.width = panelWidth;
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
+
+ if (shouldUseSplitNotificationShade()) {
+ // In order to change the constraints at runtime, all children of the Constraint Layout
+ // must have ids.
+ ensureAllViewsHaveIds(mNotificationContainerParent);
+ }
+ }
+
+ private boolean shouldUseSplitNotificationShade() {
+ return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
+ && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+ }
+
+ private static void ensureAllViewsHaveIds(ViewGroup parentView) {
+ for (int i = 0; i < parentView.getChildCount(); i++) {
+ View childView = parentView.getChildAt(i);
+ if (childView.getId() == View.NO_ID) {
+ childView.setId(View.generateViewId());
+ }
+ }
}
private void reInflateViews() {
@@ -813,6 +847,7 @@ 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/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6c4c63..6db21f9159ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -362,34 +362,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
}
@Test
- public void testAugmentTileFromStorageWithNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
- assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
- assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
- }
-
- @Test
- public void testAugmentTileFromStorageWithoutNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationDataUri()).isEqualTo(null);
- }
-
- @Test
public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8c0afb89b361..ef314ad16556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -21,6 +21,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+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;
@@ -31,26 +33,24 @@ import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
-import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final long MIN_LINGER_DURATION = 5;
- private static final String TEST_PACKAGE_A = "com.test.package_a";
+ private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
private static final String TEST_PACKAGE_B = "com.test.package_b";
private static final String TEST_CHANNEL_ID = "channel_id";
private static final String TEST_CHANNEL_NAME = "channel_name";
private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
private static final String TEST_CONVERSATION_ID = "conversation_id";
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+ private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
- private static final String NOTIFICATION_KEY = "notification_key";
- private static final String NOTIFICATION_CONTENT = "notification_content";
+ private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+ private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
- .setNotificationKey(NOTIFICATION_KEY)
+ .setPackageName(TEST_PACKAGE_A)
+ .setUid(0)
+ .setNotificationKey(NOTIFICATION_KEY + "1")
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
+ private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+ SHORTCUT_ID).setLongLabel("name").build();
private PeopleSpaceWidgetManager mManager;
@@ -119,175 +125,101 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
- private INotificationManager mINotificationManager;
+ private IPeopleManager mIPeopleManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mManager =
new PeopleSpaceWidgetManager(mContext);
- mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+ mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
+ // Default to single People tile widgets.
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
- editor.apply();
- Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+ getConversationWithShortcutId(SHORTCUT_ID));
}
@Test
- public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
- int[] widgetIdsArray = {};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
- mClock.advanceTime(MIN_LINGER_DURATION);
-
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
- }
-
- @Test
- public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+ public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
- mClock.advanceTime(MIN_LINGER_DURATION);
-
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
- }
-
- @Test
- public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
- int[] widgetIdsArray = {1};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
- mClock.advanceTime(MIN_LINGER_DURATION);
-
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
+ StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbn)
.setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
+ public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
+ int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ )
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(new SbnBuilder()
+ .setNotification(notificationWithoutShortcut)
+ .setPkg(TEST_PACKAGE_A)
+ .build())
.setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
+ mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
+ public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+ int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+ StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbnWithoutPackageName)
.setId(1));
- mClock.advanceTime(4);
- NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -298,13 +230,12 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+ public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -316,45 +247,57 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+ public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(4);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,215 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(4);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+ throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- Notification notification = new Notification.Builder(mContext)
+ Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(SHORTCUT_ID)
.build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(notificationWithoutMessagingStyle)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
- public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, times(2))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(null);
+ assertThat(tile.getNotificationContent()).isEqualTo(null);
+ assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+ any());
}
- /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
- private List<ConversationChannelWrapper> getConversationWithShortcutId() {
- List<ConversationChannelWrapper> convos = new ArrayList<>();
- ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
- convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
- "name").build());
- convos.add(convo1);
- return convos;
+ /**
+ * Returns a single conversation associated with {@code shortcutId}.
+ */
+ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ 0L, false);
+ return convo;
}
- private StatusBarNotification createConversationNotification(String shortcutId) {
- Notification notification = new Notification.Builder(mContext)
+ private Notification createMessagingStyleNotification(String shortcutId) {
+ return new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(shortcutId)
.setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
)
.build();
+ }
+
+ private StatusBarNotification createConversationNotification(String shortcutId) {
+ Notification notification = createMessagingStyleNotification(shortcutId);
return new SbnBuilder()
.setNotification(notification)
+ .setPkg(TEST_PACKAGE_A)
.build();
}
+
+ private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+ widgetEditor.apply();
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(widgetId), shortcutId);
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+ }
}
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 8d4470bb1cb6..c07ba723ab43 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
@@ -61,13 +61,16 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent;
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;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -204,6 +207,14 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private MediaDataManager mMediaDataManager;
@Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private ControlsComponent mControlsComponent;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ @Mock
private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -219,6 +230,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
mDisplayMetrics.density = 100;
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+ when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
@@ -237,6 +250,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mView.findViewById(R.id.notification_container_parent))
+ .thenReturn(mNotificationContainerParent);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
@@ -264,6 +279,11 @@ public class NotificationPanelViewTest extends SysuiTestCase {
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
+ when(mQsFrame.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+ when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
mLayoutInflater,
@@ -285,7 +305,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
new QSDetailDisplayer(),
mScrimController,
mMediaDataManager,
- mAmbientState);
+ mAmbientState,
+ mFeatureFlags,
+ mControlsComponent,
+ mBroadcastDispatcher);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
@@ -400,6 +423,25 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
+ @Test
+ public void testAllChildrenOfNotificationContainer_haveIds() {
+ when(mNotificationContainerParent.getChildCount()).thenReturn(2);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+
+ View view1 = new View(mContext);
+ view1.setId(1);
+ when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
+
+ View view2 = mock(View.class);
+ when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1);
+ assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9f064c14c62..e4a86c3744ce 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -55,7 +55,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -119,7 +117,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
-import com.android.server.policy.IconUtilities;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private boolean mSafeMode;
private int mMaxWidgetBitmapMemory;
- private IconUtilities mIconUtilities;
-
AppWidgetServiceImpl(Context context) {
mContext = context;
}
@@ -271,7 +266,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
- mIconUtilities = new IconUtilities(mContext);
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
@@ -578,44 +572,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
- private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- // Load the unbadged application icon and pass it to the widget to appear on
- // the masked view.
- Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
- UserHandle.of(providerUserId));
- PackageManager pm = userContext.getPackageManager();
- Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
- // Create a bitmap of the icon which is what the widget's remoteview requires.
- icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
- return mIconUtilities.createIconBitmap(icon);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Fail to get application icon", e);
- // Provider package removed, no need to mask its views as its state will be
- // purged very soon.
- return null;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
- PendingIntent onClickIntent) {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.work_widget_mask_view);
- if (icon != null) {
- views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
- }
- if (!showBadge) {
- views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
- }
- if (onClickIntent != null) {
- views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
- }
- return views;
- }
-
/**
* Mask the target widget belonging to the specified provider, or all active widgets
* of the provider if target widget == null.
@@ -625,59 +581,63 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (widgetCount == 0) {
return;
}
- final String providerPackage = provider.id.componentName.getPackageName();
- final int providerUserId = provider.getUserId();
- Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
- if (iconBitmap == null) {
- return;
- }
- final boolean showBadge;
- final Intent onClickIntent;
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.work_widget_mask_view);
+ ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
+ final int appUserId = provider.getUserId();
+ boolean showBadge;
+
final long identity = Binder.clearCallingIdentity();
try {
+ final Intent onClickIntent;
+
if (provider.maskedBySuspendedPackage) {
- showBadge = mUserManager.hasBadge(providerUserId);
+ showBadge = mUserManager.hasBadge(appUserId);
final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
- providerPackage, providerUserId);
+ appInfo.packageName, appUserId);
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
- providerUserId, true);
+ appUserId, true);
} else {
final SuspendDialogInfo dialogInfo =
- mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
- suspendingPackage, providerUserId);
+ mPackageManagerInternal.getSuspendedDialogInfo(
+ appInfo.packageName, suspendingPackage, appUserId);
// onUnsuspend is null because we don't want to start any activity on
// unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
- providerPackage, suspendingPackage, dialogInfo, null, null,
- providerUserId);
+ appInfo.packageName, suspendingPackage, dialogInfo, null, null,
+ appUserId);
}
} else if (provider.maskedByQuietProfile) {
showBadge = true;
- onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- providerUserId);
+ onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
} else /* provider.maskedByLockedProfile */ {
showBadge = true;
- onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- providerUserId);
+ onClickIntent = mKeyguardManager
+ .createConfirmDeviceCredentialIntent(null, null, appUserId);
if (onClickIntent != null) {
- onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
- | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ onClickIntent.setFlags(
+ FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
}
+
+ if (onClickIntent != null) {
+ views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
+ PendingIntent.getActivity(mContext, 0, onClickIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+ }
+
+ Icon icon = appInfo.icon != 0
+ ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
+ : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ views.setImageViewIcon(R.id.work_widget_app_icon, icon);
+ if (!showBadge) {
+ views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
+ }
+
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
- PendingIntent intent = null;
- if (onClickIntent != null) {
- // Rare informational activity click is okay being
- // immutable; the tradeoff is more security in exchange for
- // losing bounds-based window animations
- intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
- onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
- RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e01c4df42ff5..37d2cdc16926 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,10 +100,10 @@ java_library_static {
libs: [
"services.net",
"android.hardware.light-V2.0-java",
- "android.hardware.gnss-java",
- "android.hardware.power-java",
+ "android.hardware.gnss-V1-java",
+ "android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
- "android.hardware.vibrator-java",
+ "android.hardware.vibrator-V1-java",
"android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
@@ -128,22 +128,22 @@ java_library_static {
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
- "android.hardware.light-java",
+ "android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.1-java",
- "android.hardware.biometrics.face-java",
+ "android.hardware.biometrics.face-V1-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
- "android.hardware.biometrics.fingerprint-java",
+ "android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
- "android.hardware.rebootescrow-java",
+ "android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
- "android.hardware.power.stats-java",
+ "android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-java",
+ "dnsresolver_aidl_interface-V7-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 000000000000..5eed0b509688
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS \ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd826e3d..6886cdefc28a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -997,28 +997,6 @@ public abstract class PackageManagerInternal {
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
/**
- * Register to listen for loading progress of an installed package.
- * @param packageName The name of the installed package
- * @param callback To loading reporting progress
- * @param userId The user under which to check.
- * @return Whether the registration was successful. It can fail if the package has not been
- * installed yet.
- */
- public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback, int userId);
-
- /**
- * Unregister to stop listening to loading progress of an installed package
- * @param packageName The name of the installed package
- * @param callback To unregister
- * @return True if the callback is removed from registered callback list. False is the callback
- * does not exist on the registered callback list, which can happen if the callback has
- * already been unregistered.
- */
- public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback);
-
- /**
* Returns the string representation of a known package. For example,
* {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
*
@@ -1137,7 +1115,8 @@ public abstract class PackageManagerInternal {
*/
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler);
/**
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 000000000000..d0a2daf0905c
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 000000000000..20ebe2985a18
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.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.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+ private BundleUtils() {
+ }
+
+ /**
+ * Returns true if {@code in} is null or empty.
+ */
+ public static boolean isEmpty(@Nullable Bundle in) {
+ return (in == null) || (in.size() == 0);
+ }
+
+ /**
+ * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
+ * bundle.
+ *
+ * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+ * {@link Bundle#EMPTY})
+ */
+ public static @NonNull Bundle clone(@Nullable Bundle in) {
+ return (in != null) ? new Bundle(in) : new Bundle();
+ }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c21a78a5b16e..13f6fe6a6aa0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -120,6 +120,7 @@ import android.net.NetworkState;
import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
@@ -171,7 +172,6 @@ import android.security.KeyStore;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -192,6 +192,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.modules.utils.LocalLog;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
@@ -2030,7 +2031,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
new PrivateDnsValidationUpdate(netId,
- InetAddress.parseNumericAddress(ipAddress),
+ InetAddresses.parseNumericAddress(ipAddress),
hostname, validated)));
} catch (IllegalArgumentException e) {
loge("Error parsing ip address in validation event");
@@ -2142,8 +2143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
boolean isBackgroundRestricted) {
- return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
- isNetworkMetered, isBackgroundRestricted);
+ return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
}
/**
@@ -2726,9 +2727,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.println(nai.requestAt(i).toString());
}
pw.decreaseIndent();
- pw.println("Lingered:");
+ pw.println("Inactivity Timers:");
pw.increaseIndent();
- nai.dumpLingerTimers(pw);
+ nai.dumpInactivityTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -3323,27 +3324,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Updates the linger state from the network requests inside the NAI.
+ * Updates the inactivity state from the network requests inside the NAI.
* @param nai the agent info to update
* @param now the timestamp of the event causing this update
- * @return whether the network was lingered as a result of this update
+ * @return whether the network was inactive as a result of this update
*/
- private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
- // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
- // 2. If the network was lingering and there are now requests, unlinger it.
+ private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+ // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was inactive and there are now requests, unset inactive.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
- // one lingered request, start lingering.
- nai.updateLingerTimer();
+ // one lingered request, set inactive.
+ nai.updateInactivityTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.toShortString());
- nai.unlinger();
+ if (DBG) log("Unsetting inactive " + nai.toShortString());
+ nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+ } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
if (DBG) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
- log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+ log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
}
- nai.linger();
+ nai.setInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
return true;
}
@@ -3357,7 +3358,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
- npi.completeConnection();
sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
@@ -3482,7 +3482,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
}
- nai.clearLingerState();
+ nai.clearInactivityState();
// TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
// Currently, deleting it breaks tests that check for the default network disconnecting.
// Find out why, fix the rematch code, and delete this.
@@ -3824,7 +3824,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -5443,27 +5443,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
- private final AsyncChannel mAsyncChannel;
private final IBinder.DeathRecipient mDeathRecipient;
public final int providerId;
NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int providerId, IBinder.DeathRecipient deathRecipient) {
+ int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
this.providerId = providerId;
- mAsyncChannel = asyncChannel;
mDeathRecipient = deathRecipient;
- if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
- throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+ if (mDeathRecipient == null) {
+ throw new AssertionError("Must pass a deathRecipient");
}
}
- boolean isLegacyNetworkFactory() {
- return mAsyncChannel != null;
- }
-
void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
try {
messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5474,38 +5468,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+ sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
servingProviderId, request);
- }
}
void cancelRequest(NetworkRequest request) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
+ sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
}
void connect(Context context, Handler handler) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.connect(context, handler, messenger);
- } else {
- try {
- messenger.getBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- }
- }
- }
-
- void completeConnection() {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ try {
+ messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
}
}
}
@@ -6000,15 +5975,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
}
- @Override
- public int registerNetworkFactory(Messenger messenger, String name) {
- enforceNetworkFactoryPermission();
- NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
- nextNetworkProviderId(), null /* deathRecipient */);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
- return npi.providerId;
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -6022,10 +5988,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- if (!npi.isLegacyNetworkFactory()) {
- // Legacy NetworkFactories get their requests when their AsyncChannel connects.
- sendAllRequestsToProvider(npi);
- }
+ sendAllRequestsToProvider(npi);
}
@Override
@@ -6044,11 +6007,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
- @Override
- public void unregisterNetworkFactory(Messenger messenger) {
- unregisterNetworkProvider(messenger);
- }
-
private void handleUnregisterNetworkProvider(Messenger messenger) {
NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
if (npi == null) {
@@ -7281,7 +7239,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
- oldNetwork.clearLingerState();
+ oldNetwork.clearInactivityState();
if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
// Tear the network down.
@@ -7694,7 +7652,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if the state has not changed : the source of truth is controlled with
// NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
// called while rematching the individual networks above.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
lingeredNetworks.add(nai);
}
}
@@ -7721,7 +7679,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (nai.getLingerExpiry() > 0) {
+ if (nai.getInactivityExpiry() > 0) {
// This network has active linger timers and no requests, but is not
// lingering. Linger it.
//
@@ -7729,7 +7687,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// and became unneeded due to another network improving its score to the
// point where this network will no longer be able to satisfy any requests
// even if it validates.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
} else {
@@ -8006,7 +7964,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Notify the requests on this NAI that the network is now lingered.
private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
@@ -9143,6 +9101,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
}
+
/**
* Registers {@link QosSocketFilter} with {@link IQosCallback}.
*
@@ -9192,4 +9151,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
public void unregisterQosCallback(@NonNull final IQosCallback callback) {
mQosCallbackTracker.unregisterCallback(callback);
}
+
+ @Override
+ public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ // TODO http://b/176495594 track multiple default networks with networkPreferences
+ if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+ }
}
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e96fd390f15a..96f832d26816 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -50,6 +50,7 @@ import android.util.SparseArray;
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 java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -280,10 +281,12 @@ class TestNetworkService extends ITestNetworkManager.Stub {
// Add global routes (but as non-default, non-internet providing network)
if (allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
}
if (allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
}
final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 916bec27af39..4dce59f23a79 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -291,8 +292,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback safemodeCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -438,7 +440,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
+ final VcnSafemodeCallbackImpl safemodeCallback =
+ new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+ final Vcn newInstance =
+ mDeps.newVcn(
+ mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -536,7 +543,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
}
}
- /** Get current configuration list for testing purposes */
+ /** Get current VCNs for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
@@ -638,8 +645,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
- // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
- if (mVcns.containsKey(subGroup)) {
+ Vcn vcn = mVcns.get(subGroup);
+ if (vcn != null && vcn.isActive()) {
isVcnManagedNetwork = true;
}
}
@@ -651,4 +658,31 @@ public class VcnManagementService extends IVcnManagementService.Stub {
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}
+
+ /** Callback for signalling when a Vcn has entered Safemode. */
+ public interface VcnSafemodeCallback {
+ /** Called by a Vcn to signal that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ @NonNull private final ParcelUuid mSubGroup;
+
+ private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+ }
+
+ @Override
+ public void onEnteredSafemode() {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ notifyAllPolicyListenersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ecb915f49bc3..6ae064435da3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5632,7 +5632,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final int modeFlags, int userId) {
enforceNotIsolatedCaller("grantUriPermission");
GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
- synchronized (mProcLock) {
+ synchronized (this) {
final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
@@ -5666,7 +5666,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
final int modeFlags, int userId) {
enforceNotIsolatedCaller("revokeUriPermission");
- synchronized (mProcLock) {
+ synchronized (this) {
final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1529d71ad0c7..e7e98324605a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -300,6 +300,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_STREAM_DEVICES_CHANGED = 32;
private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
private static final int MSG_REINIT_VOLUMES = 34;
+ private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +700,9 @@ public class AudioService extends IAudioService.Stub
private long mLoweredFromNormalToVibrateTime;
// Array of Uids of valid accessibility services to check if caller is one of them
- private int[] mAccessibilityServiceUids;
private final Object mAccessibilityServiceUidsLock = new Object();
+ @GuardedBy("mAccessibilityServiceUidsLock")
+ private int[] mAccessibilityServiceUids;
// Uid of the active input method service to check if caller is the one or not.
private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -7082,6 +7084,10 @@ public class AudioService extends IAudioService.Stub
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+ case MSG_UPDATE_A11Y_SERVICE_UIDS:
+ onUpdateAccessibilityServiceUids();
+ break;
+
}
}
}
@@ -8148,6 +8154,9 @@ public class AudioService extends IAudioService.Stub
+ " FromRestrictions=" + mMicMuteFromRestrictions
+ " FromApi=" + mMicMuteFromApi
+ " from system=" + mMicMuteFromSystemCached);
+ pw.print("\n mAssistantUid="); pw.println(mAssistantUid);
+ pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid);
+ dumpAccessibilityServiceUids(pw);
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
@@ -8181,6 +8190,19 @@ public class AudioService extends IAudioService.Stub
}
}
+ private void dumpAccessibilityServiceUids(PrintWriter pw) {
+ synchronized (mSupportedSystemUsagesLock) {
+ if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+ pw.println(" Accessibility service Uids:");
+ for (int uid : mAccessibilityServiceUids) {
+ pw.println(" - " + uid);
+ }
+ } else {
+ pw.println(" No accessibility service Uids.");
+ }
+ }
+ }
+
/**
* Audio Analytics ids.
*/
@@ -8484,7 +8506,8 @@ public class AudioService extends IAudioService.Stub
mAccessibilityServiceUids = uids.toArray();
}
}
- AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+ sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+ 0, 0, null, 0);
}
}
@@ -8502,6 +8525,14 @@ public class AudioService extends IAudioService.Stub
}
}
+ private void onUpdateAccessibilityServiceUids() {
+ int[] accessibilityServiceUids;
+ synchronized (mAccessibilityServiceUidsLock) {
+ accessibilityServiceUids = mAccessibilityServiceUids;
+ }
+ AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
new file mode 100644
index 000000000000..769c47a94ae3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
+import android.hardware.biometrics.face.Cell;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollFrame;
+
+/**
+ * Utilities for converting between hardware and framework-defined AIDL models.
+ */
+final class AidlConversionUtils {
+ // Prevent instantiation.
+ private AidlConversionUtils() {}
+
+ @NonNull
+ public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) {
+ return new FaceAuthenticationFrame(convert(frame.data));
+ }
+
+ @NonNull
+ public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
+ final AuthenticationFrame convertedFrame = new AuthenticationFrame();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
+ return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+ }
+
+ @NonNull
+ public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) {
+ final EnrollmentFrame convertedFrame = new EnrollmentFrame();
+ convertedFrame.cell = convert(frame.getCell());
+ convertedFrame.stage = (byte) frame.getStage();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceDataFrame convert(@NonNull BaseFrame frame) {
+ return new FaceDataFrame(
+ frame.acquiredInfo,
+ frame.vendorCode,
+ frame.pan,
+ frame.tilt,
+ frame.distance,
+ frame.isCancellable);
+ }
+
+ @NonNull
+ public static BaseFrame convert(@NonNull FaceDataFrame frame) {
+ final BaseFrame convertedFrame = new BaseFrame();
+ convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo();
+ convertedFrame.vendorCode = frame.getVendorCode();
+ convertedFrame.pan = frame.getPan();
+ convertedFrame.tilt = frame.getTilt();
+ convertedFrame.distance = frame.getDistance();
+ convertedFrame.isCancellable = frame.isCancellable();
+ return convertedFrame;
+ }
+
+ @Nullable
+ public static FaceEnrollCell convert(@Nullable Cell cell) {
+ return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z);
+ }
+
+ @Nullable
+ public static Cell convert(@Nullable FaceEnrollCell cell) {
+ if (cell == null) {
+ return null;
+ }
+
+ final Cell convertedCell = new Cell();
+ convertedCell.x = cell.getX();
+ convertedCell.y = cell.getY();
+ convertedCell.z = cell.getZ();
+ return convertedCell;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index a7bfc4b16dc8..30577667e5e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -28,6 +28,8 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -193,6 +195,17 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
+ /**
+ * Called each time a new frame is received during face authentication.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
+ // TODO(b/178414967): Send additional frame data to the client callback.
+ final FaceDataFrame data = frame.getData();
+ onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ }
+
@Override public void onLockoutTimed(long durationMillis) {
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index afc7f6485bc9..da657b96afd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -27,6 +27,8 @@ import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.NativeHandle;
@@ -110,6 +112,17 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
}
+ /**
+ * Called each time a new frame is received during face enrollment.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
+ // TODO(b/178414967): Send additional frame data to the client callback.
+ final FaceDataFrame data = frame.getData();
+ onAcquired(data.getAcquiredInfo(), data.getVendorCode());
+ }
+
@Override
protected void startHalOperation() {
final ArrayList<Byte> token = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index f49601ab3fdc..640838c6ee04 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -45,7 +45,6 @@ import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -170,33 +169,39 @@ public class Sensor implements IBinder.DeathRecipient {
@Override
public void onAuthenticationFrame(AuthenticationFrame frame) {
- // TODO(b/174619156): propagate the frame to an AuthenticationClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceAuthenticationClient)) {
+ Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
+ Utils.getClientName(client));
return;
- }
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ }
+ if (frame == null) {
+ Slog.e(mTag, "Received null authentication frame for client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ ((FaceAuthenticationClient) client).onAuthenticationFrame(
+ AidlConversionUtils.convert(frame));
});
}
@Override
public void onEnrollmentFrame(EnrollmentFrame frame) {
- // TODO(b/174619156): propagate the frame to an EnrollmentClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
+ Utils.getClientName(client));
return;
}
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ if (frame == null) {
+ Slog.e(mTag, "Received null enrollment frame for client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
});
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb080b0b1..43d9ade67a11 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.IDnsResolver;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@ public class DnsManager {
for (String ipAddress : ipAddresses) {
try {
latestDnses.add(new Pair(hostname,
- InetAddress.parseNumericAddress(ipAddress)));
+ InetAddresses.parseNumericAddress(ipAddress)));
} catch (IllegalArgumentException e) {}
}
// Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b77681..46c49e7fc28c 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.net.BaseNetworkObserver;
-import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects;
@@ -433,7 +433,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
// clat IPv4 address itself (for those apps, it doesn't matter what
// the IP of the gateway is, only that there is one).
RouteInfo ipv4Default = new RouteInfo(
- new LinkAddress(Inet4Address.ANY, 0),
+ new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
clatAddress.getAddress(), mIface);
stacked.addRoute(ipv4Default);
stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7101e..a9a705f07ac4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -210,23 +210,23 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
- public static class LingerTimer implements Comparable<LingerTimer> {
+ public static class InactivityTimer implements Comparable<InactivityTimer> {
public final int requestId;
public final long expiryMs;
- public LingerTimer(int requestId, long expiryMs) {
+ public InactivityTimer(int requestId, long expiryMs) {
this.requestId = requestId;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
- if (!(o instanceof LingerTimer)) return false;
- LingerTimer other = (LingerTimer) o;
+ if (!(o instanceof InactivityTimer)) return false;
+ InactivityTimer other = (InactivityTimer) o;
return (requestId == other.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
return Objects.hash(requestId, expiryMs);
}
- public int compareTo(LingerTimer other) {
+ public int compareTo(InactivityTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
Integer.compare(requestId, other.requestId);
@@ -269,30 +269,31 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
*/
public static final int ARG_AGENT_SUCCESS = 1;
- // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
// was lingering or not.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
- private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+ private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
- // For fast lookups. Indexes into mLingerTimers by request ID.
- private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+ // For fast lookups. Indexes into mInactivityTimers by request ID.
+ private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
- // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
- // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
- // When the timer fires, all linger state is cleared, and if the network has no requests, it is
- // torn down.
- private WakeupMessage mLingerMessage;
+ // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+ // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+ // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+ // has no requests, it is torn down.
+ private WakeupMessage mInactivityMessage;
- // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
- private long mLingerExpiryMs;
+ // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+ // armed.
+ private long mInactivityExpiryMs;
- // Whether the network is lingering or not. Must be maintained separately from the above because
+ // Whether the network is inactive or not. Must be maintained separately from the above because
// it depends on the state of other networks and requests, which only ConnectivityService knows.
// (Example: we don't linger a network if it would become the best for a NetworkRequest if it
// validated).
- private boolean mLingering;
+ private boolean mInactive;
// This represents the quality of the network with no clear scale.
private int mScore;
@@ -898,17 +899,17 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
* ConnectivityService when the request is moved to another network with a higher score.
*/
public void lingerRequest(int requestId, long now, long duration) {
- if (mLingerTimerForRequest.get(requestId) != null) {
+ if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
}
final long expiryMs = now + duration;
- LingerTimer timer = new LingerTimer(requestId, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
- mLingerTimers.add(timer);
- mLingerTimerForRequest.put(requestId, timer);
+ InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+ mInactivityTimers.add(timer);
+ mInactivityTimerForRequest.put(requestId, timer);
}
/**
@@ -916,23 +917,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
* Returns true if the given requestId was lingering on this network, false otherwise.
*/
public boolean unlingerRequest(int requestId) {
- LingerTimer timer = mLingerTimerForRequest.get(requestId);
+ InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
- mLingerTimers.remove(timer);
- mLingerTimerForRequest.remove(requestId);
+ if (VDBG) {
+ Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+ }
+ mInactivityTimers.remove(timer);
+ mInactivityTimerForRequest.remove(requestId);
return true;
}
return false;
}
- public long getLingerExpiry() {
- return mLingerExpiryMs;
+ public long getInactivityExpiry() {
+ return mInactivityExpiryMs;
}
- public void updateLingerTimer() {
- long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
- if (newExpiry == mLingerExpiryMs) return;
+ public void updateInactivityTimer() {
+ long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+ if (newExpiry == mInactivityExpiryMs) return;
// Even if we're going to reschedule the timer, cancel it first. This is because the
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +943,52 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
// has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
if (newExpiry > 0) {
- mLingerMessage = new WakeupMessage(
+ mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
this /* obj (NetworkAgentInfo) */);
- mLingerMessage.schedule(newExpiry);
+ mInactivityMessage.schedule(newExpiry);
}
- mLingerExpiryMs = newExpiry;
+ mInactivityExpiryMs = newExpiry;
}
- public void linger() {
- mLingering = true;
+ public void setInactive() {
+ mInactive = true;
}
- public void unlinger() {
- mLingering = false;
+ public void unsetInactive() {
+ mInactive = false;
}
public boolean isLingering() {
- return mLingering;
+ return mInactive;
}
- public void clearLingerState() {
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ public void clearInactivityState() {
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
- mLingerTimers.clear();
- mLingerTimerForRequest.clear();
- updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
- mLingering = false;
+ mInactivityTimers.clear();
+ mInactivityTimerForRequest.clear();
+ // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+ updateInactivityTimer();
+ mInactive = false;
}
- public void dumpLingerTimers(PrintWriter pw) {
- for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ public void dumpInactivityTimers(PrintWriter pw) {
+ for (InactivityTimer timer : mInactivityTimers) {
+ pw.println(timer);
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba375ba1..fc2c7e01efde 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@ import android.net.DnsResolver;
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
@@ -111,6 +112,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +205,7 @@ public class Vpn {
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
private final Ikev2SessionCreator mIkev2SessionCreator;
+ private final UserManager mUserManager;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +280,10 @@ public class Vpn {
return LocalServices.getService(DeviceIdleInternal.class);
}
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return VpnConfig.getIntentForStatusPanel(context);
+ }
+
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -327,7 +334,7 @@ public class Vpn {
public InetAddress resolve(final String endpoint)
throws ExecutionException, InterruptedException {
try {
- return InetAddress.parseNumericAddress(endpoint);
+ return InetAddresses.parseNumericAddress(endpoint);
} catch (IllegalArgumentException e) {
// Endpoint is not numeric : fall through and resolve
}
@@ -405,6 +412,7 @@ public class Vpn {
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1119,7 +1127,7 @@ public class Vpn {
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
lp.addDnsServer(address);
allowIPv4 |= address instanceof Inet4Address;
allowIPv6 |= address instanceof Inet6Address;
@@ -1129,10 +1137,12 @@ public class Vpn {
lp.setHttpProxy(mConfig.proxyInfo);
if (!allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
}
if (!allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
}
// Concatenate search domains into a string.
@@ -1431,7 +1441,7 @@ public class Vpn {
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1515,7 +1525,7 @@ public class Vpn {
*/
public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1553,7 @@ public class Vpn {
*/
public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1778,7 @@ public class Vpn {
private void prepareStatusIntent() {
final long token = Binder.clearCallingIdentity();
try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+ mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1968,8 +1978,7 @@ public class Vpn {
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
- final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserId);
+ final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2004,9 +2013,8 @@ public class Vpn {
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
@Nullable Network underlying, @NonNull LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ UserInfo user = mUserManager.getUserInfo(mUserId);
+ if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 802472fcfba8..e496d77deaf5 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -16,8 +16,6 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -37,16 +35,16 @@ import java.util.Objects;
*/
public final class DeviceState {
/** Unique identifier for the device state. */
- @IntRange(from = INVALID_DEVICE_STATE)
+ @IntRange(from = 0)
private final int mIdentifier;
/** String description of the device state. */
@NonNull
private final String mName;
- public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+ public DeviceState(@IntRange(from = 0) int identifier,
@NonNull String name) {
- if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
+ if (identifier < 0) {
throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
}
mIdentifier = identifier;
@@ -54,7 +52,7 @@ public final class DeviceState {
}
/** Returns the unique identifier for the device state. */
- @IntRange(from = INVALID_DEVICE_STATE)
+ @IntRange(from = 0)
public int getIdentifier() {
return mIdentifier;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 375ec3a0f95f..984a17694e07 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,13 +17,13 @@
package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -31,6 +31,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -62,8 +64,12 @@ import java.util.Optional;
* the {@link DeviceStateProvider} to modify the current device state and communicating with the
* {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
* </p>
+ * The service also provides the {@link DeviceStateManager} API allowing clients to listen for
+ * changes in device state and submit requests to override the device state provided by the
+ * {@link DeviceStateProvider}.
*
* @see DeviceStatePolicy
+ * @see DeviceStateManager
*/
public final class DeviceStateManagerService extends SystemService {
private static final String TAG = "DeviceStateManagerService";
@@ -79,11 +85,11 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock")
private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
- // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
+ // The current committed device state. The default of UNSET will be replaced by
// the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
@NonNull
- private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
+ private DeviceState mCommittedState = new DeviceState(0, "UNSET");
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
@NonNull
@@ -91,19 +97,23 @@ public final class DeviceStateManagerService extends SystemService {
// Whether or not the policy is currently waiting to be notified of the current pending state.
@GuardedBy("mLock")
private boolean mIsPolicyWaitingForState = false;
- // The device state that is currently requested and is next to be configured and committed.
- // Can be overwritten by an override state value if requested.
- @GuardedBy("mLock")
- @NonNull
- private Optional<DeviceState> mRequestedState = Optional.empty();
- // The most recently requested override state, or empty if no override is requested.
+
+ // The device state that is set by the device state provider.
@GuardedBy("mLock")
@NonNull
- private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
+ private Optional<DeviceState> mBaseState = Optional.empty();
- // List of registered callbacks indexed by process id.
+ // List of processes registered to receive notifications about changes to device state and
+ // request status indexed by process id.
@GuardedBy("mLock")
- private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
+ private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
+ // List of override requests with the highest precedence request at the end.
+ @GuardedBy("mLock")
+ private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
+ // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
+ // of a change in status.
+ @GuardedBy("mLock")
+ private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
public DeviceStateManagerService(@NonNull Context context) {
this(context, new DeviceStatePolicyImpl(context));
@@ -148,55 +158,32 @@ public final class DeviceStateManagerService extends SystemService {
}
/**
- * Returns the requested state. The service will configure the device to match the requested
- * state when possible.
- */
- @NonNull
- Optional<DeviceState> getRequestedState() {
- synchronized (mLock) {
- return mRequestedState;
- }
- }
-
- /**
- * Overrides the current device state with the provided state.
+ * Returns the base state. The service will configure the device to match the base state when
+ * there is no active request to override the base state.
*
- * @return {@code true} if the override state is valid and supported, {@code false} otherwise.
+ * @see #getOverrideState()
*/
- boolean setOverrideState(int overrideState) {
- if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
- }
-
+ @NonNull
+ Optional<DeviceState> getBaseState() {
synchronized (mLock) {
- if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
- return false;
- }
-
- mRequestedOverrideState = getStateLocked(overrideState);
- updatePendingStateLocked();
+ return mBaseState;
}
-
- notifyPolicyIfNeeded();
- return true;
- }
-
- /**
- * Clears an override state set with {@link #setOverrideState(int)}.
- */
- void clearOverrideState() {
- setOverrideState(INVALID_DEVICE_STATE);
}
/**
- * Returns the current requested override state, or {@link Optional#empty()} if no override
- * state is requested.
+ * Returns the current override state, or {@link Optional#empty()} if no override state is
+ * requested. If an override states is present, the returned state will take precedence over
+ * the base state returned from {@link #getBaseState()}.
*/
@NonNull
Optional<DeviceState> getOverrideState() {
synchronized (mLock) {
- return mRequestedOverrideState;
+ if (mRequestRecords.isEmpty()) {
+ return Optional.empty();
+ }
+
+ OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
+ return Optional.of(topRequest.mRequestedState);
}
}
@@ -211,6 +198,17 @@ 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;
+ }
+ }
+
@VisibleForTesting
IDeviceStateManager getBinderService() {
return mBinderService;
@@ -224,22 +222,26 @@ public final class DeviceStateManagerService extends SystemService {
mDeviceStates.put(state.getIdentifier(), state);
}
- if (mRequestedState.isPresent()
- && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
- // The current requested state is no longer valid. We'll clear it here, though
+ 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.
- mRequestedState = Optional.empty();
+ mBaseState = Optional.empty();
}
- if (mRequestedOverrideState.isPresent()
- && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
- // The current override state is no longer valid. We'll clear it here and update
- // the committed state if necessary.
- mRequestedOverrideState = Optional.empty();
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
}
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
@@ -261,20 +263,37 @@ public final class DeviceStateManagerService extends SystemService {
}
/**
- * Requests that the system enter the provided {@code state}. The request may not be honored
- * under certain conditions, for example if the provided state is not supported.
+ * Requests to set the base state. The request may not be honored under certain conditions, for
+ * example if the provided state is not supported.
*
* @see #isSupportedStateLocked(int)
*/
- private void requestState(int identifier) {
+ private void setBaseState(int identifier) {
synchronized (mLock) {
- final Optional<DeviceState> requestedState = getStateLocked(identifier);
- if (requestedState.isPresent()) {
- mRequestedState = requestedState;
+ 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()) {
+ throw new IllegalArgumentException("Base state is not supported");
}
+
+ mBaseState = baseState;
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
+ }
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
@@ -290,10 +309,10 @@ public final class DeviceStateManagerService extends SystemService {
}
final DeviceState stateToConfigure;
- if (mRequestedOverrideState.isPresent()) {
- stateToConfigure = mRequestedOverrideState.get();
+ if (!mRequestRecords.isEmpty()) {
+ stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
} else {
- stateToConfigure = mRequestedState.orElse(null);
+ stateToConfigure = mBaseState.orElse(null);
}
if (stateToConfigure == null) {
@@ -360,6 +379,13 @@ public final class DeviceStateManagerService extends SystemService {
}
mCommittedState = mPendingState.get();
newState = mCommittedState.getIdentifier();
+
+ if (!mRequestRecords.isEmpty()) {
+ final OverrideRequestRecord topRequest =
+ mRequestRecords.get(mRequestRecords.size() - 1);
+ topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+ }
+
mPendingState = Optional.empty();
updatePendingStateLocked();
}
@@ -367,6 +393,9 @@ public final class DeviceStateManagerService extends SystemService {
// Notify callbacks of a change.
notifyDeviceStateChanged(newState);
+ // Notify the top request that it's active.
+ notifyRequestsOfStatusChangeIfNeeded();
+
// Try to configure the next state if needed.
notifyPolicyIfNeeded();
}
@@ -377,43 +406,69 @@ public final class DeviceStateManagerService extends SystemService {
"Attempting to notify callbacks with service lock held.");
}
- // Grab the lock and copy the callbacks.
- ArrayList<CallbackRecord> callbacks;
+ // Grab the lock and copy the process records.
+ ArrayList<ProcessRecord> registeredProcesses;
synchronized (mLock) {
- if (mCallbacks.size() == 0) {
+ if (mProcessRecords.size() == 0) {
return;
}
- callbacks = new ArrayList<>();
- for (int i = 0; i < mCallbacks.size(); i++) {
- callbacks.add(mCallbacks.valueAt(i));
+ registeredProcesses = new ArrayList<>();
+ for (int i = 0; i < mProcessRecords.size(); i++) {
+ registeredProcesses.add(mProcessRecords.valueAt(i));
+ }
+ }
+
+ // After releasing the lock, send the notifications out.
+ for (int i = 0; i < registeredProcesses.size(); i++) {
+ registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
+ }
+ }
+
+ /**
+ * Notifies all dirty requests (requests that have a change in status, but have not yet been
+ * notified) that their status has changed.
+ */
+ private void notifyRequestsOfStatusChangeIfNeeded() {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(
+ "Attempting to notify requests with service lock held.");
+ }
+
+ ArraySet<OverrideRequestRecord> dirtyRequests;
+ synchronized (mLock) {
+ if (mRequestsPendingStatusChange.isEmpty()) {
+ return;
}
+
+ dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
+ mRequestsPendingStatusChange.clear();
}
// After releasing the lock, send the notifications out.
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).notifyDeviceStateAsync(deviceState);
+ for (int i = 0; i < dirtyRequests.size(); i++) {
+ dirtyRequests.valueAt(i).notifyStatusIfNeeded();
}
}
- private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
+ private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
int currentState;
- CallbackRecord record;
+ ProcessRecord record;
// Grab the lock to register the callback and get the current state.
synchronized (mLock) {
- if (mCallbacks.contains(callingPid)) {
+ if (mProcessRecords.contains(pid)) {
throw new SecurityException("The calling process has already registered an"
+ " IDeviceStateManagerCallback.");
}
- record = new CallbackRecord(callback, callingPid);
+ record = new ProcessRecord(callback, pid);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
- mCallbacks.put(callingPid, record);
+ mProcessRecords.put(pid, record);
currentState = mCommittedState.getIdentifier();
}
@@ -421,10 +476,86 @@ public final class DeviceStateManagerService extends SystemService {
record.notifyDeviceStateAsync(currentState);
}
- private void unregisterCallbackInternal(CallbackRecord record) {
+ private void handleProcessDied(ProcessRecord processRecord) {
+ synchronized (mLock) {
+ // Cancel all requests from this process.
+ final int requestCount = processRecord.mRequestRecords.size();
+ for (int i = 0; i < requestCount; i++) {
+ final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
+ // Cancel the request but don't mark it as dirty since there's no need to send
+ // notifications if the process has died.
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
+ false /* markDirty */);
+ }
+
+ mProcessRecords.remove(processRecord.mPid);
+
+ updatePendingStateLocked();
+ }
+
+ notifyPolicyIfNeeded();
+ }
+
+ private void requestStateInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (processRecord.mRequestRecords.get(token) != null) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
+ ? null : mRequestRecords.get(mRequestRecords.size() - 1);
+ if (topRecord != null) {
+ topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
+ }
+
+ final OverrideRequestRecord request =
+ 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();
+ }
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
+ }
+
+ private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
synchronized (mLock) {
- mCallbacks.remove(record.mPid);
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
+ if (request == null) {
+ throw new IllegalStateException("No known request for the given token");
+ }
+
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+
+ updatePendingStateLocked();
}
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
}
private void dumpInternal(PrintWriter pw) {
@@ -433,15 +564,26 @@ public final class DeviceStateManagerService extends SystemService {
synchronized (mLock) {
pw.println(" mCommittedState=" + mCommittedState);
pw.println(" mPendingState=" + mPendingState);
- pw.println(" mRequestedState=" + mRequestedState);
- pw.println(" mRequestedOverrideState=" + mRequestedOverrideState);
+ pw.println(" mBaseState=" + mBaseState);
+ pw.println(" mOverrideState=" + getOverrideState());
- final int callbackCount = mCallbacks.size();
+ final int processCount = mProcessRecords.size();
pw.println();
- pw.println("Callbacks: size=" + callbackCount);
- for (int i = 0; i < callbackCount; i++) {
- CallbackRecord callback = mCallbacks.valueAt(i);
- pw.println(" " + i + ": mPid=" + callback.mPid);
+ pw.println("Registered processes: size=" + processCount);
+ for (int i = 0; i < processCount; i++) {
+ ProcessRecord processRecord = mProcessRecords.valueAt(i);
+ pw.println(" " + i + ": mPid=" + processRecord.mPid);
+ }
+
+ final int requestCount = mRequestRecords.size();
+ pw.println();
+ pw.println("Override requests: size=" + requestCount);
+ for (int i = 0; i < requestCount; i++) {
+ OverrideRequestRecord requestRecord = mRequestRecords.get(i);
+ pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
+ + ", mRequestedState=" + requestRecord.mRequestedState
+ + ", mFlags=" + requestRecord.mFlags
+ + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
}
}
}
@@ -452,12 +594,6 @@ public final class DeviceStateManagerService extends SystemService {
if (newDeviceStates.length == 0) {
throw new IllegalArgumentException("Supported device states must not be empty");
}
- for (int i = 0; i < newDeviceStates.length; i++) {
- if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
- throw new IllegalArgumentException(
- "Supported device states includes INVALID_DEVICE_STATE identifier");
- }
- }
updateSupportedStates(newDeviceStates);
}
@@ -467,22 +603,24 @@ public final class DeviceStateManagerService extends SystemService {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
- requestState(identifier);
+ setBaseState(identifier);
}
}
- private final class CallbackRecord implements IBinder.DeathRecipient {
+ private final class ProcessRecord implements IBinder.DeathRecipient {
private final IDeviceStateManagerCallback mCallback;
private final int mPid;
- CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
+ private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
+
+ ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
mCallback = callback;
mPid = pid;
}
@Override
public void binderDied() {
- unregisterCallbackInternal(this);
+ handleProcessDied(this);
}
public void notifyDeviceStateAsync(int devicestate) {
@@ -493,6 +631,119 @@ public final class DeviceStateManagerService extends SystemService {
ex);
}
}
+
+ public void notifyRequestActiveAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestActive(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestSuspended(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestCanceled(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+ }
+
+ /** A record describing a request to override the state of the device. */
+ private final class OverrideRequestRecord {
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ @Nullable
+ public String statusToString(int status) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ return "ACTIVE";
+ case STATUS_SUSPENDED:
+ return "SUSPENDED";
+ case STATUS_CANCELED:
+ return "CANCELED";
+ case STATUS_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return null;
+ }
+ }
+
+ private final ProcessRecord mProcessRecord;
+ @NonNull
+ private final IBinder mToken;
+ @NonNull
+ private final DeviceState mRequestedState;
+ private final int mFlags;
+
+ private int mStatus = STATUS_UNKNOWN;
+ private int mLastNotifiedStatus = STATUS_UNKNOWN;
+
+ OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
+ @NonNull DeviceState requestedState, int flags) {
+ mProcessRecord = processRecord;
+ mToken = token;
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public void setStatusLocked(int status) {
+ setStatusLocked(status, true /* markDirty */);
+ }
+
+ public void setStatusLocked(int status, boolean markDirty) {
+ if (mStatus != status) {
+ if (mStatus == STATUS_CANCELED) {
+ throw new IllegalStateException(
+ "Can not alter the status of a request after set to CANCELED.");
+ }
+
+ mStatus = status;
+
+ if (mStatus == STATUS_CANCELED) {
+ mRequestRecords.remove(this);
+ mProcessRecord.mRequestRecords.remove(mToken);
+ }
+
+ if (markDirty) {
+ mRequestsPendingStatusChange.add(this);
+ }
+ }
+ }
+
+ public void notifyStatusIfNeeded() {
+ int stateToReport;
+ synchronized (mLock) {
+ if (mLastNotifiedStatus == mStatus) {
+ return;
+ }
+
+ stateToReport = mStatus;
+ mLastNotifiedStatus = mStatus;
+ }
+
+ if (stateToReport == STATUS_ACTIVE) {
+ mProcessRecord.notifyRequestActiveAsync(this);
+ } else if (stateToReport == STATUS_SUSPENDED) {
+ mProcessRecord.notifyRequestSuspendedAsync(this);
+ } else if (stateToReport == STATUS_CANCELED) {
+ mProcessRecord.notifyRequestCanceledAsync(this);
+ }
+ }
}
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
@@ -506,13 +757,59 @@ public final class DeviceStateManagerService extends SystemService {
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid);
+ registerProcess(callingPid, callback);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@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.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestStateInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelRequest(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to clear requested device state.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelRequestInternal(callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 7914531f9910..6cc55a6c4774 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,6 +16,13 @@
package com.android.server.devicestate;
+import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.Binder;
import android.os.ShellCommand;
import java.io.PrintWriter;
@@ -27,10 +34,15 @@ import java.util.Optional;
* Use with {@code adb shell cmd device_state ...}.
*/
public class DeviceStateManagerShellCommand extends ShellCommand {
- private final DeviceStateManagerService mInternal;
+ @Nullable
+ private static DeviceStateRequest sLastRequest;
+
+ private final DeviceStateManagerService mService;
+ private final DeviceStateManager mClient;
public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
- mInternal = service;
+ mService = service;
+ mClient = service.getContext().getSystemService(DeviceStateManager.class);
}
@Override
@@ -51,15 +63,15 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
}
private void printState(PrintWriter pw) {
- DeviceState committedState = mInternal.getCommittedState();
- Optional<DeviceState> requestedState = mInternal.getRequestedState();
- Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
+ DeviceState committedState = mService.getCommittedState();
+ Optional<DeviceState> baseState = mService.getBaseState();
+ Optional<DeviceState> overrideState = mService.getOverrideState();
pw.println("Committed state: " + committedState);
- if (requestedOverrideState.isPresent()) {
+ if (overrideState.isPresent()) {
pw.println("----------------------");
- pw.println("Base state: " + requestedState.orElse(null));
- pw.println("Override state: " + requestedOverrideState.get());
+ pw.println("Base state: " + baseState.orElse(null));
+ pw.println("Override state: " + overrideState.get());
}
}
@@ -67,32 +79,51 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
final String nextArg = getNextArg();
if (nextArg == null) {
printState(pw);
- } else if ("reset".equals(nextArg)) {
- mInternal.clearOverrideState();
- } else {
- int requestedState;
- try {
- requestedState = Integer.parseInt(nextArg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: requested state should be an integer");
- return -1;
- }
+ }
- boolean success = mInternal.setOverrideState(requestedState);
- if (!success) {
- getErrPrintWriter().println("Error: failed to set override state. Run:");
- getErrPrintWriter().println("");
- getErrPrintWriter().println(" print-states");
- getErrPrintWriter().println("");
- getErrPrintWriter().println("to get the list of currently supported device states");
- return -1;
+ final Context context = mService.getContext();
+ context.enforceCallingOrSelfPermission(
+ CONTROL_DEVICE_STATE,
+ "Permission required to request device state.");
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ sLastRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestState(request, null /* executor */, null /* callback */);
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ }
+
+ sLastRequest = request;
}
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
}
+
return 0;
}
private int runPrintStates(PrintWriter pw) {
- DeviceState[] states = mInternal.getSupportedStates();
+ DeviceState[] states = mService.getSupportedStates();
pw.print("Supported states: [\n");
for (int i = 0; i < states.length; i++) {
pw.print(" " + states[i] + ",\n");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e5151d84c33e..0950d5dd076f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -514,8 +514,8 @@ public final class DisplayManagerService extends SystemService {
DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(),
- new HandlerExecutor(mHandler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+ new DeviceStateListener());
scheduleTraversalLocked(false);
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index afc97c7fd803..dac94f6aa9d2 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -27,6 +27,7 @@ import android.os.FileUtils;
import android.system.ErrnoException;
import android.system.Os;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Slog;
@@ -39,7 +40,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -116,7 +116,7 @@ final class UpdatableFontDir {
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+ private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
FsverityUtil fsverityUtil) {
@@ -205,7 +205,7 @@ final class UpdatableFontDir {
*/
public void update(List<FontUpdateRequest> requests) throws SystemFontException {
// Backup the mapping for rollback.
- HashMap<String, FontFileInfo> backupMap = new HashMap<>(mFontFileInfoMap);
+ ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
long backupLastModifiedDate = mLastModifiedDate;
boolean success = false;
try {
@@ -464,7 +464,7 @@ final class UpdatableFontDir {
}
Map<String, File> getFontFileMap() {
- Map<String, File> map = new HashMap<>();
+ Map<String, File> map = new ArrayMap<>();
for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
map.put(entry.getKey(), entry.getValue().getFile());
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b53501d6e3..d8e124a00104 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -300,45 +299,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
- /**
- * Debug flag for overriding runtime {@link SystemProperties}.
- */
- @AnyThread
- private static final class DebugFlag {
- private static final Object LOCK = new Object();
- private final String mKey;
- private final boolean mDefaultValue;
- @GuardedBy("LOCK")
- private boolean mValue;
-
- public DebugFlag(String key, boolean defaultValue) {
- mKey = key;
- mDefaultValue = defaultValue;
- mValue = SystemProperties.getBoolean(key, defaultValue);
- }
-
- void refresh() {
- synchronized (LOCK) {
- mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
- }
- }
-
- boolean value() {
- synchronized (LOCK) {
- return mValue;
- }
- }
- }
-
- /**
- * Debug flags that can be overridden using "adb shell setprop <key>"
- * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
- */
- private static final class DebugFlags {
- static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
- new DebugFlag("debug.optimize_startinput", false);
- }
-
@UserIdInt
private int mLastSwitchUserId;
@@ -3687,12 +3647,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
- || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+ } else {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else {
- res = InputBindResult.NO_EDITOR;
}
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -5467,10 +5424,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@ShellCommandResult
private int onCommandWithSystemIdentity(@Nullable String cmd) {
- if ("refresh_debug_properties".equals(cmd)) {
- return refreshDebugProperties();
- }
-
if ("get-last-switch-user-id".equals(cmd)) {
return mService.getLastSwitchUserId(this);
}
@@ -5505,13 +5458,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- @ShellCommandResult
- private int refreshDebugProperties() {
- DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
- return ShellCommandResult.SUCCESS;
- }
-
- @BinderThread
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d7408153..b5746bbf310a 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
import android.os.SystemClock;
import android.util.TimeUtils;
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b8b54b3a42e7..8d73518bced1 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -31,7 +31,7 @@ import android.location.util.identity.CallerIdentity;
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
-import com.android.server.utils.eventlog.LocalEventLog;
+import com.android.server.location.eventlog.LocalEventLog;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dcd77ef..39ed7e8b1e1a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
package com.android.server.net;
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@ public abstract class NetworkPolicyManagerInternal {
public abstract void resetUserState(int userId);
/**
- * Figure out if networking is blocked for a given set of conditions.
- *
- * This is used by ConnectivityService via passing stale copies of conditions, so it must not
- * take any locks.
- *
- * @param uid The target uid.
- * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
- * @param isNetworkMetered True if the network is metered.
- * @param isBackgroundRestricted True if data saver is enabled.
- *
- * @return true if networking is blocked for the UID under the specified conditions.
- */
- public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted) {
- // Log of invoking internal function is disabled because it will be called very
- // frequently. And metrics are unlikely needed on this method because the callers are
- // external and this method doesn't take any locks or perform expensive operations.
- return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, null);
- }
-
- /**
* Informs that an appId has been added or removed from the temp-powersave-allowlist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ca21640feb8f..b99a55271943 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5402,6 +5402,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+ // Log of invoking this function is disabled because it will be called very frequently. And
+ // metrics are unlikely needed on this method because the callers are external and this
+ // method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ @Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
@@ -5410,9 +5421,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
isBackgroundRestricted = mRestrictBackground;
}
- //TODO(b/177490332): The logic here might not be correct because it doesn't consider
- // RULE_REJECT_METERED condition. And it could be replaced by
- // isUidNetworkingBlockedInternal().
+ // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+ // RULE_REJECT_METERED condition. And it could be replaced by
+ // isUidNetworkingBlockedInternal().
return isBackgroundRestricted
&& !hasRule(uidRules, RULE_ALLOW_METERED)
&& !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 000000000000..a83edb75badb
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+ private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+ private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final TombstoneWatcher mWatcher;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<TombstoneFile> mTombstones;
+
+ NativeTombstoneManager(Context context) {
+ mTombstones = new SparseArray<TombstoneFile>();
+ mContext = context;
+
+ final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ thread.start();
+ mHandler = thread.getThreadHandler();
+
+ mWatcher = new TombstoneWatcher();
+ mWatcher.startWatching();
+ }
+
+ void onSystemReady() {
+ // Scan existing tombstones.
+ mHandler.post(() -> {
+ final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+ for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+ if (tombstoneFiles[i].isFile()) {
+ handleTombstone(tombstoneFiles[i]);
+ }
+ }
+ });
+ }
+
+ private void handleTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.startsWith("tombstone_")) {
+ return;
+ }
+
+ if (filename.endsWith(".pb")) {
+ handleProtoTombstone(path);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path);
+ }
+ }
+
+ private void handleProtoTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.endsWith(".pb")) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ final String suffix = filename.substring("tombstone_".length());
+ final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+ int number;
+ try {
+ number = Integer.parseInt(numberStr);
+ if (number < 0 || number > 99) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+ } catch (NumberFormatException ex) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+ } catch (FileNotFoundException ex) {
+ Slog.w(TAG, "failed to open " + path, ex);
+ return;
+ }
+
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+ if (!parsedTombstone.isPresent()) {
+ IoUtils.closeQuietly(pfd);
+ return;
+ }
+
+ synchronized (mLock) {
+ TombstoneFile previous = mTombstones.get(number);
+ if (previous != null) {
+ previous.dispose();
+ }
+
+ mTombstones.put(number, parsedTombstone.get());
+ }
+ }
+
+ static class TombstoneFile {
+ final ParcelFileDescriptor mPfd;
+
+ final @UserIdInt int mUserId;
+ final @AppIdInt int mAppId;
+
+ boolean mPurged = false;
+
+ TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ mPfd = pfd;
+ mUserId = userId;
+ mAppId = appId;
+ }
+
+ public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+ if (mPurged) {
+ return false;
+ }
+
+ if (userId.isPresent() && userId.get() != mUserId) {
+ return false;
+ }
+
+ if (appId.isPresent() && appId.get() != mAppId) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void dispose() {
+ IoUtils.closeQuietly(mPfd);
+ }
+
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int uid = 0;
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed to parse tombstone", ex);
+ return Optional.empty();
+ }
+
+ if (!UserHandle.isApp(uid)) {
+ Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+ return Optional.empty();
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+
+ if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+ Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+ return Optional.empty();
+ }
+
+ return Optional.of(new TombstoneFile(pfd, userId, appId));
+ }
+ }
+
+ class TombstoneWatcher extends FileObserver {
+ TombstoneWatcher() {
+ // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+ // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+ // isn't supported (MOVED_TO).
+ super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+ }
+
+ @Override
+ public void onEvent(int event, @Nullable String path) {
+ mHandler.post(() -> {
+ handleTombstone(new File(TOMBSTONE_DIR, path));
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 000000000000..cb3c7ff0c07d
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+ private static final String TAG = "NativeTombstoneManagerService";
+
+ private NativeTombstoneManager mManager;
+
+ public NativeTombstoneManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mManager = new NativeTombstoneManager(getContext());
+ LocalServices.addService(NativeTombstoneManager.class, mManager);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mManager.onSystemReady();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f996d7f8..66ea55401cef 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@ import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKE
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@ public class ApkChecksums {
/**
* Fetch or calculate checksums for the collection of files.
*
- * @param filesToChecksum split name, null for base and File to fetch checksums for
- * @param optional mask to fetch readily available checksums
- * @param required mask to forcefully calculate if not available
- * @param installerPackageName package name of the installer of the packages
- * @param trustedInstallers array of certificate to trust, two specific cases:
- * null - trust anybody,
- * [] - trust nobody.
- * @param statusReceiver to receive the resulting checksums
+ * @param filesToChecksum split name, null for base and File to fetch checksums for
+ * @param optional mask to fetch readily available checksums
+ * @param required mask to forcefully calculate if not available
+ * @param installerPackageName package name of the installer of the packages
+ * @param trustedInstallers array of certificate to trust, two specific cases:
+ * null - trust anybody,
+ * [] - trust nobody.
+ * @param onChecksumsReadyListener to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
@Checksum.Type int optional,
@Checksum.Type int required,
@Nullable String installerPackageName,
@Nullable Certificate[] trustedInstallers,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector) {
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@ public class ApkChecksums {
}
long startTime = SystemClock.uptimeMillis();
- processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
- startTime);
+ processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+ injector, startTime);
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
List<Map<Integer, ApkChecksum>> result,
@Checksum.Type int required,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
@@ -350,7 +349,7 @@ public class ApkChecksums {
// Not ready, come back later.
injector.getHandler().postDelayed(() -> {
processRequiredChecksums(filesToChecksum, result, required,
- statusReceiver, injector, startTime);
+ onChecksumsReadyListener, injector, startTime);
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
return;
}
@@ -363,13 +362,9 @@ public class ApkChecksums {
}
}
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_CHECKSUMS,
- allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
try {
- statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
+ onChecksumsReadyListener.onChecksumsReady(allChecksums);
+ } catch (RemoteException e) {
Slog.w(TAG, e);
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 000000000000..a32e107d9e73
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * 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.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+ @IntDef({
+ Direction.TO_PARENT,
+ Direction.TO_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Direction {
+ int TO_PARENT = 0;
+ int TO_PROFILE = 1;
+ }
+
+ /** The intent filter that's used */
+ public final IntentFilter filter;
+
+ /**
+ * The flags related to the forwarding, e.g.
+ * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+ * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+ */
+ public final int flags;
+
+ /**
+ * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+ * {@link Direction#TO_PROFILE}.
+ */
+ public final @Direction int direction;
+
+ /**
+ * Whether this cross profile intent filter would allow personal data to be shared into
+ * the work profile. If this is {@code true}, this intent filter should be only added to
+ * the profile if the admin does not enable
+ * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+ */
+ public final boolean letsPersonalDataIntoProfile;
+
+ private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+ @Direction int direction, boolean letsPersonalDataIntoProfile) {
+ this.filter = requireNonNull(filter);
+ this.flags = flags;
+ this.direction = direction;
+ this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ static final class Builder {
+ private IntentFilter mFilter = new IntentFilter();
+ private int mFlags;
+ private @Direction int mDirection;
+ private boolean mLetsPersonalDataIntoProfile;
+
+ Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+ mDirection = direction;
+ mFlags = flags;
+ mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ Builder addAction(String action) {
+ mFilter.addAction(action);
+ return this;
+ }
+
+ Builder addCategory(String category) {
+ mFilter.addCategory(category);
+ return this;
+ }
+
+ Builder addDataType(String type) {
+ try {
+ mFilter.addDataType(type);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ // ignore
+ }
+ return this;
+ }
+
+ Builder addDataScheme(String scheme) {
+ mFilter.addDataScheme(scheme);
+ return this;
+ }
+
+ DefaultCrossProfileIntentFilter build() {
+ return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+ mLetsPersonalDataIntoProfile);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 000000000000..3019439a430b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+ private DefaultCrossProfileIntentFiltersUtils() {
+ }
+
+ // Intents from profile to parent user
+ /** Emergency call intent with mime type is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Emergency call intent with data schemes is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either managed profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** Pressing the call button can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_BUTTON)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** SMS and MMS are exclusively handled by the primary user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ /** Mobile network settings is always shown in the primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ MOBILE_NETWORK_SETTINGS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+ .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** HOME intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter HOME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_HOME)
+ .build();
+
+ /** Get content can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Open document intent can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_OPEN_DOCUMENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick for any data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick without data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Speech recognition can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(ACTION_RECOGNIZE_SPEECH)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Media capture can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+ .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Alarm setting can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter SET_ALARM =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(AlarmClock.ACTION_SET_ALARM)
+ .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+ .addAction(AlarmClock.ACTION_SET_TIMER)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ // Intents from parent to profile user
+
+ /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+ private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_SEND)
+ .addAction(Intent.ACTION_SEND_MULTIPLE)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** USB devices attached can get forwarded to the profile. */
+ private static final DefaultCrossProfileIntentFilter
+ USB_DEVICE_ATTACHED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+ return Arrays.asList(
+ EMERGENCY_CALL_MIME,
+ EMERGENCY_CALL_DATA,
+ DIAL_MIME,
+ DIAL_DATA,
+ DIAL_RAW,
+ CALL_BUTTON,
+ SMS_MMS,
+ SET_ALARM,
+ MEDIA_CAPTURE,
+ RECOGNIZE_SPEECH,
+ ACTION_PICK_RAW,
+ ACTION_PICK_DATA,
+ OPEN_DOCUMENT,
+ GET_CONTENT,
+ USB_DEVICE_ATTACHED,
+ ACTION_SEND,
+ HOME,
+ MOBILE_NETWORK_SETTINGS);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index f5ec595cc45a..ecafdfdbd6f1 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -249,24 +249,6 @@ public final class IncrementalStates {
}
/**
- * @return the current startable state.
- */
- public boolean isStartable() {
- synchronized (mLock) {
- return mStartableState.isStartable();
- }
- }
-
- /**
- * @return Whether the package is still being loaded or has been fully loaded.
- */
- public boolean isLoading() {
- synchronized (mLock) {
- return mLoadingState.isLoading();
- }
- }
-
- /**
* @return all current states in a Parcelable.
*/
public IncrementalStatesInfo getIncrementalStatesInfo() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c4a23f961072..f240d85572c4 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1316,10 +1316,6 @@ public class LauncherAppsService extends SystemService {
} finally {
mListeners.finishBroadcast();
}
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- pmi.registerInstalledLoadingProgressCallback(packageName,
- new PackageLoadingProgressCallback(packageName, user),
- user.getIdentifier());
super.onPackageAdded(packageName, uid);
}
@@ -1542,38 +1538,5 @@ public class LauncherAppsService extends SystemService {
checkCallbackCount();
}
}
-
- class PackageLoadingProgressCallback extends
- PackageManagerInternal.InstalledLoadingProgressCallback {
- private String mPackageName;
- private UserHandle mUser;
-
- PackageLoadingProgressCallback(String packageName, UserHandle user) {
- super(mCallbackHandler);
- mPackageName = packageName;
- mUser = user;
- }
-
- @Override
- public void onLoadingProgressChanged(float progress) {
- final int n = mListeners.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
- continue;
- }
- try {
- listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
- }
- } finally {
- mListeners.finishBroadcast();
- }
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f444ed6df2a3..5d4fa1e1c123 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@ import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
-import static android.content.Intent.CATEGORY_BROWSABLE;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
@@ -68,11 +67,7 @@ import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_APEX;
@@ -177,6 +172,7 @@ import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -377,11 +373,6 @@ import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
@@ -395,6 +386,11 @@ import com.android.server.pm.permission.LegacyPermissionManagerService;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -404,7 +400,6 @@ import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseBooleanArray;
-import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -466,7 +461,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.function.Supplier;
/**
* Keep track of all those APKs everywhere.
@@ -5523,19 +5517,19 @@ public class PackageManagerService extends IPackageManager.Stub
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId) {
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
- statusReceiver, userId, mInjector.getBackgroundExecutor(),
+ onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
- @Checksum.Type int optional,
- @Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
- @NonNull Handler handler) {
+ @Checksum.Type int optional, @Checksum.Type int required,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+ @NonNull Executor executor, @NonNull Handler handler) {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(executor);
Objects.requireNonNull(handler);
@@ -5571,7 +5565,7 @@ public class PackageManagerService extends IPackageManager.Stub
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
- trustedCerts, statusReceiver, injector);
+ trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -8948,6 +8942,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<String> getAllPackages() {
+ enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers");
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mLock) {
@@ -19168,6 +19163,8 @@ public class PackageManagerService extends IPackageManager.Stub
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null);
+ // Unregister progress listener
+ mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
// Unregister health listener as it will always be healthy from now
mIncrementalManager.unregisterHealthListener(codePath);
}
@@ -27045,47 +27042,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public boolean registerInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
- final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
- userId);
- if (ps == null) {
- return false;
- }
- if (!ps.isPackageLoading()) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Package is fully loaded.");
- return false;
- }
- if (mIncrementalManager == null) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Incremental is not enabled");
- return false;
- }
- return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
- public boolean unregisterInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback) {
- final PackageSetting ps;
- synchronized (mLock) {
- ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.w(TAG, "Failed unregistering loading progress callback. Package "
- + packageName + " is not installed");
- return false;
- }
- }
- if (mIncrementalManager == null) {
- return false;
- }
- return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
public IncrementalStatesInfo getIncrementalStatesInfo(
@NonNull String packageName, int filterCallingUid, int userId) {
final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
@@ -27110,12 +27066,14 @@ public class PackageManagerService extends IPackageManager.Stub
ps.setStatesOnCrashOrAnr();
}
+ @Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
requestChecksumsInternal(packageName, includeSplits, optional, required,
- trustedInstallers, statusReceiver, userId, executor, handler);
+ trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 69e84b536004..ca5d2b49b99d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -344,8 +344,8 @@ public class PackageSetting extends PackageSettingBase {
installSource.originatingPackageName);
proto.end(sourceToken);
}
- proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
- proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
+ proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable());
+ proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading());
writeUsersInfoToProto(proto, PackageProto.USERS);
writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
proto.end(packageToken);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 8aa553d68b98..3a142837e063 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,8 +26,6 @@ import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.UninstallReason;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
@@ -42,8 +40,6 @@ import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
@@ -731,14 +727,14 @@ public abstract class PackageSettingBase extends SettingBase {
* @return True if package is startable, false otherwise.
*/
public boolean isPackageStartable() {
- return incrementalStates.isStartable();
+ return getIncrementalStates().isStartable();
}
/**
* @return True if package is still being loaded, false if the package is fully loaded.
*/
public boolean isPackageLoading() {
- return incrementalStates.isLoading();
+ return getIncrementalStates().isLoading();
}
/**
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c9163843..8c5084afcdf9 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
import android.app.admin.SecurityLog;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -39,7 +33,6 @@ import android.util.Slog;
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -109,26 +102,23 @@ public final class ProcessLoggingHandler extends Handler {
// Capturing local loggingInfo to still log even if hash was invalidated.
try {
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
- new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ new IOnChecksumsReadyListener.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- processChecksums(loggingInfo, intent);
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ processChecksums(loggingInfo, checksums);
}
- }), context.getUserId(), mExecutor, this);
+ }, context.getUserId(),
+ mExecutor, this);
} catch (Throwable t) {
Slog.e(TAG, "requestChecksums() failed", t);
enqueueProcessChecksum(loggingInfo, null);
}
}
- void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
- Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
- ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
- ApkChecksum[].class);
-
- for (ApkChecksum checksum : checksums) {
+ void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+ for (int i = 0, size = checksums.size(); i < size; ++i) {
+ ApkChecksum checksum = checksums.get(i);
if (checksum.getType() == CHECKSUM_TYPE) {
processChecksum(loggingInfo, checksum.getValue());
return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca47deb0..e5a70c3a2365 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@ public class RestrictionsSet {
if (!changed) {
return false;
}
- if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+ if (!BundleUtils.isEmpty(restrictions)) {
mUserRestrictions.put(userId, restrictions);
} else {
mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d685c247..c17d2e4b0c62 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@ public abstract class UserManagerInternal {
* Gets all {@link UserInfo UserInfos}.
*/
public abstract @NonNull UserInfo[] getUserInfos();
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}.
+ */
+ public abstract void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a8077774d62a..753f22da2b9d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@ import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
import android.os.storage.StorageManager;
+import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@ public class UserManagerService extends IUserManager.Stub {
final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
- if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+ if (BundleUtils.isEmpty(global) && local.isEmpty()) {
// Common case first.
return baseRestrictions;
}
- final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+ final Bundle effective = BundleUtils.clone(baseRestrictions);
UserRestrictionsUtils.merge(effective, global);
UserRestrictionsUtils.merge(effective, local.mergeAll());
@@ -2105,7 +2107,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
- return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+ return BundleUtils.clone(getEffectiveUserRestrictions(userId));
}
@Override
@@ -2129,7 +2131,7 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userId));
newRestrictions.putBoolean(key, value);
@@ -2727,7 +2729,7 @@ public class UserManagerService extends IUserManager.Stub {
if (userVersion < 7) {
// Previously only one user could enforce global restrictions, now it is per-user.
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+ if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
&& mDeviceOwnerUserId != UserHandle.USER_NULL) {
mDevicePolicyGlobalUserRestrictions.updateRestrictions(
mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@ public class UserManagerService extends IUserManager.Stub {
t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
+ applyDefaultUserSettings(userTypeDetails, userId);
+ setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
if (preCreate) {
// Must start user (which will be stopped right away, through
// UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@ public class UserManagerService extends IUserManager.Stub {
return userInfo;
}
+ private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+ final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+ final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+ if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+ return;
+ }
+
+ final int systemSettingsSize = systemSettings.size();
+ final String[] systemSettingsArray = systemSettings.keySet().toArray(
+ new String[systemSettingsSize]);
+ for (int i = 0; i < systemSettingsSize; i++) {
+ final String setting = systemSettingsArray[i];
+ if (!Settings.System.putStringForUser(
+ mContext.getContentResolver(), setting, systemSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+ }
+ }
+
+ final int secureSettingsSize = secureSettings.size();
+ final String[] secureSettingsArray = secureSettings.keySet().toArray(
+ new String[secureSettingsSize]);
+ for (int i = 0; i < secureSettingsSize; i++) {
+ final String setting = secureSettingsArray[i];
+ if (!Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), setting, secureSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+ }
+ }
+ }
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+ */
+ private void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+ Bundle profileRestrictions, @UserIdInt int parentUserId) {
+ if (profileDetails == null || !profileDetails.isProfile()) {
+ return;
+ }
+ final List<DefaultCrossProfileIntentFilter> filters =
+ profileDetails.getDefaultCrossProfileIntentFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+ final boolean disallowSharingIntoProfile =
+ profileRestrictions.getBoolean(
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+ /* defaultValue = */ false);
+ final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+ for (int i = 0; i < size; i++) {
+ final DefaultCrossProfileIntentFilter filter =
+ profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+ if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+ continue;
+ }
+ if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+ filter.flags);
+ } else {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+ filter.flags);
+ }
+ }
+ }
+
/**
* Finds and converts a previously pre-created user into a regular user, if possible.
*
@@ -5462,6 +5539,15 @@ public class UserManagerService extends IUserManager.Stub {
return allInfos;
}
}
+
+ @Override
+ public void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+ final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+ UserManagerService.this.setDefaultCrossProfileIntentFilters(
+ profileUserId, userTypeDetails, restrictions, parentUserId);
+ }
}
/**
@@ -5726,8 +5812,8 @@ public class UserManagerService extends IUserManager.Stub {
// merge existing base restrictions with the new type's default restrictions
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userInfo.id));
UserRestrictionsUtils.merge(newRestrictions,
newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff4063850..ff90043bec94 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@ import android.util.TypedXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@ public class UserRestrictionsUtils {
return in != null ? in : new Bundle();
}
- public static boolean isEmpty(@Nullable Bundle in) {
- return (in == null) || (in.size() == 0);
- }
-
/**
* Returns {@code true} if given bundle is not null and contains {@code true} for a given
* restriction.
@@ -396,17 +393,6 @@ public class UserRestrictionsUtils {
return in != null && in.getBoolean(restriction);
}
- /**
- * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
- * bundle.
- *
- * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
- * {@link Bundle#EMPTY})
- */
- public static @NonNull Bundle clone(@Nullable Bundle in) {
- return (in != null) ? new Bundle(in) : new Bundle();
- }
-
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
Objects.requireNonNull(dest);
Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@ public class UserRestrictionsUtils {
if (a == b) {
return true;
}
- if (isEmpty(a)) {
- return isEmpty(b);
+ if (BundleUtils.isEmpty(a)) {
+ return BundleUtils.isEmpty(b);
}
- if (isEmpty(b)) {
+ if (BundleUtils.isEmpty(b)) {
return false;
}
for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9e4635..17ce386e3770 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@ import android.os.Bundle;
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@ public final class UserTypeDetails {
*/
private final @Nullable Bundle mDefaultRestrictions;
+ /**
+ * List of {@link android.provider.Settings.System} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSystemSettings;
+
+ /**
+ * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSecureSettings;
+
+ /**
+ * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+ * profiles.
+ */
+ private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
// Fields for profiles only, controlling the nature of their badges.
// All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@ public final class UserTypeDetails {
int iconBadge, int badgePlain, int badgeNoBackground,
@Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@Nullable int[] darkThemeBadgeColors,
- @Nullable Bundle defaultRestrictions) {
+ @Nullable Bundle defaultRestrictions,
+ @Nullable Bundle defaultSystemSettings,
+ @Nullable Bundle defaultSecureSettings,
+ @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@ public final class UserTypeDetails {
this.mBaseType = baseType;
this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
this.mDefaultRestrictions = defaultRestrictions;
+ this.mDefaultSystemSettings = defaultSystemSettings;
+ this.mDefaultSecureSettings = defaultSecureSettings;
+ this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
this.mIconBadge = iconBadge;
this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@ public final class UserTypeDetails {
return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
}
- /** Returns a Bundle representing the default user restrictions. */
+ /** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
- return UserRestrictionsUtils.clone(mDefaultRestrictions);
+ return BundleUtils.clone(mDefaultRestrictions);
}
/** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@ public final class UserTypeDetails {
UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
}
+ /** Returns a {@link Bundle} representing the default system settings. */
+ @NonNull Bundle getDefaultSystemSettings() {
+ return BundleUtils.clone(mDefaultSystemSettings);
+ }
+
+ /** Returns a {@link Bundle} representing the default secure settings. */
+ @NonNull Bundle getDefaultSecureSettings() {
+ return BundleUtils.clone(mDefaultSecureSettings);
+ }
+
+ /** Returns a list of default cross profile intent filters. */
+ @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+ return mDefaultCrossProfileIntentFilters != null
+ ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+ : Collections.emptyList();
+ }
+
+
/** Dumps details of the UserTypeDetails. Do not parse this. */
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@ public final class UserTypeDetails {
private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
private int mDefaultUserInfoPropertyFlags = 0;
private @Nullable Bundle mDefaultRestrictions = null;
+ private @Nullable Bundle mDefaultSystemSettings = null;
+ private @Nullable Bundle mDefaultSecureSettings = null;
+ private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+ null;
private boolean mEnabled = true;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@ public final class UserTypeDetails {
return this;
}
+ public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+ mDefaultSystemSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+ mDefaultSecureSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultCrossProfileIntentFilters(
+ @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+ mDefaultCrossProfileIntentFilters = intentFilters;
+ return this;
+ }
+
@UserInfoFlag int getBaseType() {
return mBaseType;
}
@@ -425,17 +491,28 @@ public final class UserTypeDetails {
Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
"UserTypeDetails " + mName + " has badge but no badgeColors.");
}
+ if (!isProfile()) {
+ Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+ || mDefaultCrossProfileIntentFilters.isEmpty(),
+ "UserTypeDetails %s has a non empty "
+ + "defaultCrossProfileIntentFilters", mName);
+ }
return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions);
+ mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters);
}
private boolean hasBadge() {
return mIconBadge != Resources.ID_NULL;
}
+ private boolean isProfile() {
+ return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+ }
+
// TODO(b/143784345): Refactor this when we clean up UserInfo.
private boolean hasValidBaseType() {
return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf608c6d7..6aac0b2f3d0f 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@ public final class UserTypeFactory {
com.android.internal.R.color.profile_badge_1_dark,
com.android.internal.R.color.profile_badge_2_dark,
com.android.internal.R.color.profile_badge_3_dark)
- .setDefaultRestrictions(null);
+ .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+ .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
}
/**
@@ -256,12 +258,35 @@ public final class UserTypeFactory {
return restrictions;
}
+ private static Bundle getDefaultManagedProfileRestrictions() {
+ final Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+ return restrictions;
+ }
+
+ private static Bundle getDefaultManagedProfileSecureSettings() {
+ // Only add String values to the bundle, settings are written as Strings eventually
+ final Bundle settings = new Bundle();
+ settings.putString(
+ android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+ settings.putString(
+ android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+ return settings;
+ }
+
+ private static List<DefaultCrossProfileIntentFilter>
+ getDefaultManagedCrossProfileIntentFilter() {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+ }
+
/**
* Reads the given xml parser to obtain device user-type customization, and updates the given
* map of {@link UserTypeDetails.Builder}s accordingly.
* <p>
* The xml file can specify the attributes according to the set... methods below.
*/
+ // TODO(b/176973369): Add parsing logic to support custom settings/filters
+ // in config_user_types.xml
@VisibleForTesting
static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index c10e828d8c3d..82fc22c51afc 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,8 +74,8 @@ class DisplayFoldController {
mHandler = handler;
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context),
- new HandlerExecutor(handler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+ new DeviceStateListener(context));
}
void finishedGoingToSleep() {
diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java
deleted file mode 100644
index 884d7d497a8f..000000000000
--- a/services/core/java/com/android/server/policy/IconUtilities.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.policy;
-
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.TableMaskFilter;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.content.res.Resources;
-import android.content.Context;
-
-/**
- * Various utilities shared amongst the Launcher's classes.
- */
-public final class IconUtilities {
-
- private int mIconWidth = -1;
- private int mIconHeight = -1;
- private int mIconTextureWidth = -1;
- private int mIconTextureHeight = -1;
-
- private final Rect mOldBounds = new Rect();
- private final Canvas mCanvas = new Canvas();
- private final DisplayMetrics mDisplayMetrics;
-
- private ColorFilter mDisabledColorFilter;
-
- public IconUtilities(Context context) {
- final Resources resources = context.getResources();
- DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
- final float density = metrics.density;
- final float blurPx = 5 * density;
-
- mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
- mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
- mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
- }
-
- /**
- * Returns a bitmap suitable for the all apps view. The bitmap will be a power
- * of two sized ARGB_8888 bitmap that can be used as a gl texture.
- */
- public Bitmap createIconBitmap(Drawable icon) {
- int width = mIconWidth;
- int height = mIconHeight;
-
- if (icon instanceof PaintDrawable) {
- PaintDrawable painter = (PaintDrawable) icon;
- painter.setIntrinsicWidth(width);
- painter.setIntrinsicHeight(height);
- } else if (icon instanceof BitmapDrawable) {
- // Ensure the bitmap has a density.
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(mDisplayMetrics);
- }
- }
- int sourceWidth = icon.getIntrinsicWidth();
- int sourceHeight = icon.getIntrinsicHeight();
-
- if (sourceWidth > 0 && sourceHeight > 0) {
- // There are intrinsic sizes.
- if (width < sourceWidth || height < sourceHeight) {
- // It's too big, scale it down.
- final float ratio = (float) sourceWidth / sourceHeight;
- if (sourceWidth > sourceHeight) {
- height = (int) (width / ratio);
- } else if (sourceHeight > sourceWidth) {
- width = (int) (height * ratio);
- }
- } else if (sourceWidth < width && sourceHeight < height) {
- // It's small, use the size they gave us.
- width = sourceWidth;
- height = sourceHeight;
- }
- }
-
- // no intrinsic size --> use default size
- int textureWidth = mIconTextureWidth;
- int textureHeight = mIconTextureHeight;
-
- final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = mCanvas;
- canvas.setBitmap(bitmap);
-
- final int left = (textureWidth-width) / 2;
- final int top = (textureHeight-height) / 2;
-
- mOldBounds.set(icon.getBounds());
- icon.setBounds(left, top, left+width, top+height);
- icon.draw(canvas);
- icon.setBounds(mOldBounds);
-
- return bitmap;
- }
-
- public ColorFilter getDisabledColorFilter() {
- if (mDisabledColorFilter != null) {
- return mDisabledColorFilter;
- }
- ColorMatrix brightnessMatrix = new ColorMatrix();
- float brightnessF = 0.5f;
- int brightnessI = (int) (255 * brightnessF);
- // Brightness: C-new = C-old*(1-amount) + amount
- float scale = 1f - brightnessF;
- float[] mat = brightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = brightnessI;
- mat[9] = brightnessI;
- mat[14] = brightnessI;
-
- ColorMatrix filterMatrix = new ColorMatrix();
- filterMatrix.setSaturation(0);
- filterMatrix.preConcat(brightnessMatrix);
-
- mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
- return mDisabledColorFilter;
- }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e1b7df..bc819617332d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@ import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
import java.io.File;
import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
}
});
+
mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
new StateCallback() {
@Override
@@ -3136,7 +3138,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (keyguardGoingAway) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d64406432a..c77e266ee11a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1158,7 +1158,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* @param startTime the start time of the animation in uptime milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c7930b89..a95628f633ad 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@ import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
@@ -398,7 +399,7 @@ public class KeyguardServiceDelegate {
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index a82f239948ff..5ec527a7d6c4 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -29,6 +29,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -37,6 +38,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -82,10 +84,19 @@ public class Vcn extends Handler {
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+ /**
+ * Causes this VCN to immediately enter Safemode.
+ *
+ * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ */
+ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
+ @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -94,14 +105,33 @@ public class Vcn extends Handler {
@NonNull private VcnConfig mConfig;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ /**
+ * Whether this Vcn instance is active and running.
+ *
+ * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+ * shut down or has entered safe mode.
+ *
+ * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+ * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+ * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+ * Looper.
+ */
+ // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+ private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ config,
+ snapshot,
+ vcnSafemodeCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -110,10 +140,13 @@ public class Vcn extends Handler {
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mVcnSafemodeCallback =
+ Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -143,6 +176,11 @@ public class Vcn extends Handler {
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
+ /** Synchronously checks whether this Vcn is active. */
+ public boolean isActive() {
+ return mIsActive.get();
+ }
+
/** Get current Gateways for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Set<VcnGatewayConnection> getVcnGatewayConnections() {
@@ -160,7 +198,7 @@ public class Vcn extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
- if (!mIsRunning) {
+ if (!isActive()) {
return;
}
@@ -177,6 +215,9 @@ public class Vcn extends Handler {
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
+ case MSG_CMD_ENTER_SAFEMODE:
+ handleEnterSafemode();
+ break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -198,7 +239,13 @@ public class Vcn extends Handler {
gatewayConnection.teardownAsynchronously();
}
- mIsRunning = false;
+ mIsActive.set(false);
+ }
+
+ private void handleEnterSafemode() {
+ handleTeardown();
+
+ mVcnSafemodeCallback.onEnteredSafemode();
}
private void handleNetworkRequested(
@@ -233,7 +280,8 @@ public class Vcn extends Handler {
mVcnContext,
mSubscriptionGroup,
mLastSnapshot,
- gatewayConnectionConfig);
+ gatewayConnectionConfig,
+ new VcnGatewayStatusCallbackImpl());
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
@@ -242,7 +290,7 @@ public class Vcn extends Handler {
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
- if (mIsRunning) {
+ if (isActive()) {
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
@@ -271,6 +319,20 @@ public class Vcn extends Handler {
return 52;
}
+ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public interface VcnGatewayStatusCallback {
+ /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ @Override
+ public void onEnteredSafemode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -279,9 +341,14 @@ public class Vcn extends Handler {
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- VcnGatewayConnectionConfig connectionConfig) {
+ VcnGatewayConnectionConfig connectionConfig,
+ VcnGatewayStatusCallback gatewayStatusCallback) {
return new VcnGatewayConnection(
- vcnContext, subscriptionGroup, snapshot, connectionConfig);
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 853bb4324f90..9ecdf1b48789 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,7 +61,6 @@ import android.os.ParcelUuid;
import android.util.ArraySet;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
@@ -69,6 +68,7 @@ import com.android.internal.util.StateMachine;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import java.io.IOException;
import java.net.Inet4Address;
@@ -403,15 +403,13 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
- @NonNull private final Object mLock = new Object();
-
- @GuardedBy("mLock")
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+ @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -487,8 +485,15 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnGatewayConnectionConfig connectionConfig) {
- this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -497,16 +502,17 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+ mGatewayStatusCallback =
+ Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
- synchronized (mLock) {
- mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
- }
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
@@ -577,13 +583,10 @@ public class VcnGatewayConnection extends StateMachine {
*/
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
Objects.requireNonNull(snapshot, "Missing snapshot");
+ mVcnContext.ensureRunningOnLooperThread();
- // Vcn is the only user of this method and runs on the same Thread, but lock around
- // mLastSnapshot to be technically correct.
- synchronized (mLock) {
- mLastSnapshot = snapshot;
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
- }
+ mLastSnapshot = snapshot;
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ce951b85ffe6..771b712cf480 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -975,7 +975,8 @@ class ActivityClientController extends IActivityClientController.Stub {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null, null);
+ packageName, enterAnim, exitAnim, null, null,
+ r.mOverrideTaskTransition);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 36c503703b9c..f29b57ff9305 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
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_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -682,6 +678,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean mRequestForceTransition;
boolean mEnteringAnimation;
+ boolean mOverrideTaskTransition;
boolean mAppStopped;
// A hint to override the window specified rotation animation, or -1 to use the window specified
@@ -1365,7 +1362,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
final boolean surfaceReady = w.isDrawn() // Regular case
- || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
updateRoundedCorners(w);
@@ -1627,6 +1623,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (rotationAnimation >= 0) {
mRotationAnimationHint = rotationAnimation;
}
+
+ mOverrideTaskTransition = options.getOverrideTaskTransition();
}
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3997,7 +3995,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener());
+ pendingOptions.getAnimationFinishedListener(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6775,20 +6774,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
- // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
- // size compat mode.
- if (providesMaxBounds()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
- + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
- + "mode %s", getUid(),
- resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
- !matchParentBounds(), inSizeCompatMode());
- }
- resolvedConfig.windowConfiguration
- .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
- }
}
/**
@@ -6972,19 +6957,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return super.getBounds();
}
- @Override
- public boolean providesMaxBounds() {
- // System and SystemUI should always be able to access the physical display bounds,
- // so do not provide it with the overridden maximum bounds.
- // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
- if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
- getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
- return false;
- }
- // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
- return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d846c3a6d36c..79f8229c6162 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1814,8 +1814,7 @@ class ActivityStarter {
}
private Task computeTargetTask() {
- if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
- && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// A new task should be created instead of using existing one.
return null;
} else if (mSourceRecord != null) {
@@ -2505,7 +2504,9 @@ class ActivityStarter {
// If bring to front is requested, and no result is requested and we have not been given
// an explicit task to launch in to, and we can find a task that was started with this
// same component, then instead of launching bring that one to the front.
- putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+ putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)
+ ? (mInTask == null && mStartActivity.resultTo == null)
+ : (mInTask == null);
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index fde036950245..c589feae56ca 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -39,7 +39,7 @@ import android.net.Uri;
import android.os.Bundle;
import com.android.internal.R;
-import com.android.server.policy.IconUtilities;
+import com.android.internal.util.ImageUtils;
/** Displays an ongoing notification for a process displaying an alert window */
class AlertWindowNotification {
@@ -54,7 +54,6 @@ class AlertWindowNotification {
private final NotificationManager mNotificationManager;
private final String mPackageName;
private boolean mPosted;
- private IconUtilities mIconUtilities;
AlertWindowNotification(WindowManagerService service, String packageName) {
mService = service;
@@ -63,7 +62,6 @@ class AlertWindowNotification {
(NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
mNotificationTag = CHANNEL_PREFIX + mPackageName;
mRequestCode = sNextRequestCode++;
- mIconUtilities = new IconUtilities(mService.mContext);
}
void post() {
@@ -126,8 +124,9 @@ class AlertWindowNotification {
if (aInfo != null) {
final Drawable drawable = pm.getApplicationIcon(aInfo);
- if (drawable != null) {
- final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+ int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
+ if (bitmap != null) {
builder.setLargeIcon(bitmap);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 90070c8f5068..5b685b4a0499 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
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.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
@@ -257,6 +258,7 @@ public class AppTransition implements Dump {
private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
private int mLastClipRevealMaxTranslation;
@@ -266,6 +268,7 @@ public class AppTransition implements Dump {
private final boolean mLowRamRecentsEnabled;
private final int mDefaultWindowAnimationStyleResId;
+ private boolean mOverrideTaskTransition;
private RemoteAnimationController mRemoteAnimationController;
@@ -445,7 +448,7 @@ public class AppTransition implements Dump {
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
if (mRemoteAnimationController != null) {
- mRemoteAnimationController.goodToGo();
+ mRemoteAnimationController.goodToGo(transit);
}
return redoLayout;
}
@@ -508,6 +511,11 @@ public class AppTransition implements Dump {
mListeners.remove(listener);
}
+ void registerKeygaurdExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ mKeyguardExitAnimationStartListener = listener;
+ }
+
public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -971,7 +979,8 @@ public class AppTransition implements Dump {
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, WindowContainer container) {
- if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) {
+ if (mNextAppTransitionOverrideRequested
+ && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
}
@@ -1175,7 +1184,8 @@ public class AppTransition implements Dump {
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
+ IRemoteCallback startedCallback, IRemoteCallback endedCallback,
+ boolean overrideTaskTransaction) {
if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionOverrideRequested = true;
@@ -1185,6 +1195,7 @@ public class AppTransition implements Dump {
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
mAnimationFinishedCallback = endedCallback;
+ mOverrideTaskTransition = overrideTaskTransaction;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 10a44987fe7d..9ca7eca07dc6 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@ public class AppTransitionController {
private final DisplayContent mDisplayContent;
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+ private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
@@ -437,10 +438,14 @@ public class AppTransitionController {
return adapter;
}
}
- if (mRemoteAnimationDefinition == null) {
- return null;
+ if (mRemoteAnimationDefinition != null) {
+ final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+ transit, activityTypes);
+ if (adapter != null) {
+ return adapter;
+ }
}
- return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+ return null;
}
/**
@@ -709,6 +714,13 @@ public class AppTransitionController {
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
+ for (int i = 0; i < openingApps.size(); ++i) {
+ openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+ for (int i = 0; i < closingApps.size(); ++i) {
+ closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 02e281f512a2..61fe023ae1b9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,7 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -35,7 +35,7 @@ import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -1074,7 +1074,7 @@ public class DisplayPolicy {
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
+ mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
break;
case TYPE_NAVIGATION_BAR:
@@ -1101,7 +1101,7 @@ public class DisplayPolicy {
(displayFrames, windowState, inOutFrame) ->
inOutFrame.set(windowState.getFrame()));
- mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
+ mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
inOutFrame.top -= mBottomGestureAdditionalInset;
});
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a91595..99c9e798c605 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@ public class Letterbox {
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+ if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072c0de7..392f27ead2bb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@ class RemoteAnimationController implements DeathRecipient {
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
- void goodToGo() {
+ void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@ class RemoteAnimationController implements DeathRecipient {
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+ // TODO(bc-unlock): Create the remote non app animation targets (if any)
+ final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+ wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -274,6 +279,7 @@ class RemoteAnimationController implements DeathRecipient {
private void setRunningRemoteAnimation(boolean running) {
final int pid = mRemoteAnimationAdapter.getCallingPid();
final int uid = mRemoteAnimationAdapter.getCallingUid();
+
if (pid == 0) {
throw new RuntimeException("Calling pid of remote animation was null");
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 61fec0d0ead9..2c97f783e14d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -847,8 +847,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
@@ -865,6 +863,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
@@ -933,7 +933,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
- win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction());
} while (i > 0);
mWmService.mDestroySurface.clear();
}
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6df453684734..65671959e884 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -253,6 +253,20 @@ public class SafeActivityOptions {
throw new SecurityException(msg);
}
+ // Check if the caller is allowed to override any app transition animation.
+ final boolean overrideTaskTransition = options.getOverrideTaskTransition();
+ if (aInfo != null && overrideTaskTransition) {
+ final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
+ START_TASKS_FROM_RECENTS, callingPid, callingUid);
+ if (startTasksFromRecentsPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with overrideTaskTransition=true";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
// Check permission for remote animations
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
if (adapter != null && supervisor.mService.checkPermission(
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30af0eda17fc..e44a028c897f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@ import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -921,10 +919,8 @@ class Task extends WindowContainer<WindowContainer> {
return;
}
- if (isLeafTask()) {
- // This task is going away, so save the last state if necessary.
- saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
- }
+ // This task is going away, so save the last state if necessary.
+ saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -2459,14 +2455,16 @@ class Task extends WindowContainer<WindowContainer> {
/**
* Saves launching state if necessary so that we can launch the activity to its latest state.
- * It only saves state if this task has been shown to user and it's in fullscreen or freeform
- * mode on freeform displays.
*/
private void saveLaunchingStateIfNeeded() {
saveLaunchingStateIfNeeded(getDisplayContent());
}
private void saveLaunchingStateIfNeeded(DisplayContent display) {
+ if (!isLeafTask()) {
+ return;
+ }
+
if (!getHasBeenVisible()) {
// Not ever visible to user.
return;
@@ -2867,16 +2865,6 @@ class Task extends WindowContainer<WindowContainer> {
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
- // Since the task is letterboxed due to mismatched orientation against its parent,
- // sandbox max bounds to the app bounds.
- if (!outBounds.isEmpty()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
- + "orientation with parent, to %s vs DisplayArea %s", outBounds,
- getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
- }
- getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
- }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 41854072985c..63732d8d4bdc 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@ import android.content.Intent;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -905,6 +906,13 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
}
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopMostActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
SurfaceControl getSplitScreenDividerAnchor() {
return mSplitScreenDividerAnchor;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23beaf6..98eb11f8a970 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
dc.mWallpaperController.startWallpaperAnimation(anim);
}
}
- }
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
dc.startKeyguardExitOnNonAppWindows(
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 2a5e969f9256..91a6664e0b36 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -220,8 +220,6 @@ public class WindowAnimator {
mRemoveReplacedWindows = false;
}
- mService.destroyPreservedSurfaceLocked();
-
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a33b8becb488..2c92fd035def 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -26,9 +26,11 @@ import android.hardware.display.DisplayManagerInternal;
import android.os.IBinder;
import android.view.Display;
import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
@@ -189,6 +191,21 @@ public abstract class WindowManagerInternal {
}
/**
+ * An interface to be notified when keyguard exit animation should start.
+ */
+ public interface KeyguardExitAnimationStartListener {
+ /**
+ * Called when keyguard exit animation should start.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ IRemoteAnimationFinishedCallback finishedCallback);
+ }
+
+ /**
* An interface to be notified about hardware keyboard status.
*/
public interface OnHardKeyboardStatusChangeListener {
@@ -412,6 +429,14 @@ public abstract class WindowManagerInternal {
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Registers a listener to be notified to start the keyguard exit animation.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener);
+
+ /**
* Reports that the password for the given user has changed.
*/
public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4ff13cd30398..0894b2660769 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -83,7 +83,6 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
-import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -405,6 +404,8 @@ public class WindowManagerService extends IWindowManager.Stub
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ static final int LOGTAG_INPUT_FOCUS = 62001;
+
/**
* Restrict ability of activities overriding transition animation in a way such that
* an activity can do it only when the transition happens within a same task.
@@ -413,7 +414,6 @@ public class WindowManagerService extends IWindowManager.Stub
*/
private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
"persist.wm.disable_custom_task_animation";
- static final int LOGTAG_INPUT_FOCUS = 62001;
/**
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +421,19 @@ public class WindowManagerService extends IWindowManager.Stub
static boolean sDisableCustomTaskAnimationProperty =
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -626,13 +639,6 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
- * Windows with a preserved surface waiting to be destroyed. These windows
- * are going through a surface change. We keep the old surface around until
- * the first frame on the new surface finishes drawing.
- */
- final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
-
- /**
* This is set when we have run out of memory, and will either be an empty
* list or contain windows that need to be force removed.
*/
@@ -2330,7 +2336,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
- winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
@@ -5505,14 +5510,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- void destroyPreservedSurfaceLocked() {
- for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
- final WindowState w = mDestroyPreservedSurface.get(i);
- w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction());
- }
- mDestroyPreservedSurface.clear();
- }
-
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
@@ -7770,6 +7767,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ synchronized (mGlobalLock) {
+ getDefaultDisplayContentLocked().mAppTransition
+ .registerKeygaurdExitAnimationStartListener(listener);
+ }
+ }
+
+ @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 298e31421461..c1f2d02804ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -104,7 +104,6 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -2243,7 +2242,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
disposeInputChannel();
- mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
mTmpTransaction.apply();
mSession.windowRemovedLocked();
@@ -3309,11 +3307,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return destroyedSomething;
}
- if (appStopped || mWindowRemovalAllowed) {
- mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction);
- mTmpTransaction.apply();
- }
-
if (mDestroying) {
ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ " destroySurfaces: appStopped=%b"
@@ -5001,23 +4994,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- // When we change the Surface size, in scenarios which may require changing
- // the surface position in sync with the resize, we use a preserved surface
- // so we can freeze it while waiting for the client to report draw on the newly
- // sized surface. At the moment this logic is only in place for switching
- // in and out of the big surface for split screen resize.
if (isDragResizeChanged()) {
setDragResizing();
- // We can only change top level windows to the full-screen surface when
- // resizing (as we only have one full-screen surface). So there is no need
- // to preserve and destroy windows which are attached to another, they
- // will keep their surface and its size may change over time.
- if (mHasSurface && !isChildWindow()) {
- mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
- result |= RELAYOUT_RES_SURFACE_CHANGED |
- RELAYOUT_RES_FIRST_TIME;
- scheduleAnimation();
- }
}
final boolean freeformResizing = isDragResizing()
&& getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 95660ed572d9..2da3dda831e1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -116,15 +116,7 @@ class WindowStateAnimator {
boolean mAnimationIsEntrance;
WindowSurfaceController mSurfaceController;
- private WindowSurfaceController mPendingDestroySurface;
- /**
- * Set if the client has asked that the destroy of its surface be delayed
- * until it explicitly says it is okay.
- */
- boolean mSurfaceDestroyDeferred;
-
- private boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -257,11 +249,6 @@ class WindowStateAnimator {
//dump();
mLastHidden = true;
- // We may have a preserved surface which we no longer need. If there was a quick
- // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark
- // it to be destroyed in prepareSurfaceLocked.
- markPreservedSurfaceForDestroy();
-
if (mSurfaceController != null) {
mSurfaceController.hide(transaction, reason);
}
@@ -323,70 +310,6 @@ class WindowStateAnimator {
return result;
}
- void preserveSurfaceLocked(SurfaceControl.Transaction t) {
- if (mDestroyPreservedSurfaceUponRedraw) {
- // This could happen when switching the surface mode very fast. For example,
- // we preserved a surface when dragResizing changed to true. Then before the
- // preserved surface is removed, dragResizing changed to false again.
- // In this case, we need to leave the preserved surface alone, and destroy
- // the actual surface, so that the createSurface call could create a surface
- // of the proper size. The preserved surface will still be removed when client
- // finishes drawing to the new surface.
- mSurfaceDestroyDeferred = false;
-
- // Make sure to reparent any children of the new surface back to the preserved
- // surface before destroying it.
- if (mSurfaceController != null && mPendingDestroySurface != null) {
- mPostDrawTransaction.reparentChildren(
- mSurfaceController.mSurfaceControl,
- mPendingDestroySurface.mSurfaceControl).apply();
- }
- destroySurfaceLocked(t);
- mSurfaceDestroyDeferred = true;
- return;
- }
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
- if (mSurfaceController != null) {
- // Our SurfaceControl is always at layer 0 within the parent Surface managed by
- // window-state. We want this old Surface to stay on top of the new one
- // until we do the swap, so we place it at a positive layer.
- t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
- }
- mDestroyPreservedSurfaceUponRedraw = true;
- mSurfaceDestroyDeferred = true;
- destroySurfaceLocked(t);
- }
-
- void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) {
- if (!mDestroyPreservedSurfaceUponRedraw) {
- return;
- }
-
- // If we are preserving a surface but we aren't relaunching that means
- // we are just doing an in-place switch. In that case any SurfaceFlinger side
- // child layers need to be reparented to the new surface to make this
- // transparent to the app.
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (mSurfaceController != null && mPendingDestroySurface != null
- && !mPendingDestroySurface.mChildrenDetached
- && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl).apply();
- }
-
- destroyDeferredSurfaceLocked(t);
- mDestroyPreservedSurfaceUponRedraw = false;
- }
-
- private void markPreservedSurfaceForDestroy() {
- if (mDestroyPreservedSurfaceUponRedraw
- && !mService.mDestroyPreservedSurface.contains(mWin)) {
- mService.mDestroyPreservedSurface.add(mWin);
- }
- }
-
void resetDrawState() {
mDrawState = DRAW_PENDING;
@@ -508,39 +431,23 @@ class WindowStateAnimator {
return;
}
- // When destroying a surface we want to make sure child windows are hidden. If we are
- // preserving the surface until redraw though we intend to swap it out with another surface
- // for resizing. In this case the window always remains visible to the user and the child
- // windows should likewise remain visible.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWin.mHidden = true;
- }
+ mWin.mHidden = true;
try {
- if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
- + mSurfaceController + ", session " + mSession);
- if (mSurfaceDestroyDeferred) {
- if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- }
- mPendingDestroySurface = mSurfaceController;
- }
- } else {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- destroySurface(t);
+ if (DEBUG_VISIBILITY) {
+ logWithStack(TAG, "Window " + this + " destroying surface "
+ + mSurfaceController + ", session " + mSession);
}
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
+ destroySurface(t);
// Don't hide wallpaper if we're deferring the surface destroy
// because of a surface change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
+ mWallpaperControllerLocked.hideWallpapers(mWin);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+ + " surface " + mSurfaceController + " session " + mSession + ": "
+ + e.toString());
}
// Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
@@ -554,27 +461,6 @@ class WindowStateAnimator {
mDrawState = NO_SURFACE;
}
- void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) {
- try {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- // Don't hide wallpaper if we're destroying a deferred surface
- // after a surface mode change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception thrown when destroying Window "
- + this + " surface " + mPendingDestroySurface
- + " session " + mSession + ": " + e.toString());
- }
- mSurfaceDestroyDeferred = false;
- mPendingDestroySurface = null;
- }
-
void computeShownFrameLocked() {
if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
@@ -744,7 +630,6 @@ class WindowStateAnimator {
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
if (showSurfaceRobustlyLocked(t)) {
- markPreservedSurfaceForDestroy();
mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
if (mIsWallpaper) {
@@ -905,20 +790,6 @@ class WindowStateAnimator {
if (!shown)
return false;
- // If we had a preserved surface it's no longer needed, and it may be harmful
- // if we are transparent.
- if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
- final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
- mPostDrawTransaction.reparent(pendingSurfaceControl, null);
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (!mPendingDestroySurface.mChildrenDetached) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl);
- }
- }
-
t.merge(mPostDrawTransaction);
return true;
}
@@ -1058,13 +929,6 @@ class WindowStateAnimator {
pw.println();
}
- if (mPendingDestroySurface != null) {
- pw.print(prefix); pw.print("mPendingDestroySurface=");
- pw.println(mPendingDestroySurface);
- }
- if (mSurfaceDestroyDeferred) {
- pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
- }
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f4477df021c..82ba3c188b76 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -55,8 +55,6 @@ class WindowSurfaceController {
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
private float mSurfaceY = 0;
- private int mSurfaceW = 0;
- private int mSurfaceH = 0;
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -82,9 +80,6 @@ class WindowSurfaceController {
int flags, WindowStateAnimator animator, int windowType) {
mAnimator = animator;
- mSurfaceW = w;
- mSurfaceH = h;
-
title = name;
mService = animator.mService;
@@ -104,8 +99,8 @@ class WindowSurfaceController {
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
- final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+ final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
if (useBLAST) {
b.setBLASTLayer();
@@ -119,7 +114,6 @@ class WindowSurfaceController {
void hide(SurfaceControl.Transaction transaction, String reason) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
- mAnimator.destroyPreservedSurfaceLocked(transaction);
if (mSurfaceShown) {
hideSurface(transaction);
}
@@ -335,9 +329,7 @@ class WindowSurfaceController {
pw.print(" layer="); pw.print(mSurfaceLayer);
pw.print(" alpha="); pw.print(mSurfaceAlpha);
pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY);
- pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.print(mSurfaceH);
+ pw.print(","); pw.print(mSurfaceY); pw.print(") ");
pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
pw.print(", "); pw.print(mLastDtdy); pw.println(")");
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index cc7e00a43a6e..91be0564a26f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -134,7 +134,7 @@ cc_defaults {
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-cpp",
+ "android.hardware.gnss-V1-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -147,12 +147,12 @@ cc_defaults {
"android.hardware.light@2.0",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-cpp",
+ "android.hardware.power-V1-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-ndk_platform",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
- "android.hardware.vibrator-unstable-cpp",
+ "android.hardware.vibrator-V2-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -162,7 +162,7 @@ cc_defaults {
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
- "android.system.suspend.control-cpp",
+ "android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend@1.0",
"service.incremental",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 11e4db503ec5..160033edb093 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
@@ -127,4 +128,7 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
public void provisionFullyManagedDevice(
FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
}
+
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d2e5de0c19b..06d737fa7ca8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -313,6 +313,7 @@ import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.Owners.OwnerDto;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.RestrictionsSet;
@@ -910,12 +911,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
};
protected static class RestrictionsListener implements UserRestrictionsListener {
- private Context mContext;
-
- public RestrictionsListener(Context context) {
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final DevicePolicyManagerService mDpms;
+
+ public RestrictionsListener(
+ Context context,
+ UserManagerInternal userManagerInternal,
+ DevicePolicyManagerService dpms) {
mContext = context;
+ mUserManagerInternal = userManagerInternal;
+ mDpms = dpms;
}
+ @Override
public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
Bundle prevRestrictions) {
final boolean newlyDisallowed =
@@ -925,13 +934,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
if (restrictionChanged) {
- // Notify ManagedProvisioning to update the built-in cross profile intent filters.
- Intent intent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- intent.setPackage(getManagedProvisioningPackage(mContext));
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ final int parentId = mUserManagerInternal.getProfileParentId(userId);
+ if (parentId == userId) {
+ return;
+ }
+
+ // Always reset filters on the parent user, which handles cross profile intent
+ // filters between the parent and its profiles.
+ Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+ + "change");
+ mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+ mContext.sendBroadcastAsUser(new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+ UserHandle.of(userId));
}
}
}
@@ -1116,6 +1131,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
}
+ // Used by DevicePolicyManagerServiceShellCommand
+ List<OwnerDto> listAllOwners() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+ List<OwnerDto> owners = mOwners.listAllOwners();
+ synchronized (getLockObject()) {
+ for (int i = 0; i < owners.size(); i++) {
+ OwnerDto owner = owners.get(i);
+ owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
+ }
+ }
+
+ return owners;
+ }
+
/**
* Unit test will subclass it to inject mocks.
*/
@@ -1621,7 +1652,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mSetupContentObserver = new SetupContentObserver(mHandler);
- mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserRestrictionsListener(
+ new RestrictionsListener(mContext, mUserManagerInternal, this));
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
@@ -13451,15 +13483,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mOwners.hasDeviceOwner()) {
return false;
}
- if (userId == mOwners.getDeviceOwnerUserId()) {
- // The user that the DO is installed on is always affiliated with the device.
- return true;
- }
if (userId == UserHandle.USER_SYSTEM) {
// The system user is always affiliated in a DO device,
// even if in headless system user mode.
return true;
}
+ if (userId == mOwners.getDeviceOwnerUserId()) {
+ // The user that the DO is installed on is always affiliated with the device.
+ return true;
+ }
final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
@@ -16008,19 +16040,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
- markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
- restrictRemovalOfManagedProfile(admin, userInfo.id);
+ setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
}
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -16250,21 +16272,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void markIsProfileOwnerOnOrganizationOwnedDevice(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
- }
-
- private void restrictRemovalOfManagedProfile(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).addUserRestriction(
- admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+ private void setProfileOwnerOnOrgOwnedDeviceState(
+ ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+ synchronized (getLockObject()) {
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+ }
+ restrictRemovalOfManagedProfile(parentUserId);
}
- private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
- final Context profileContext = mContext.createContextAsUser(
- UserHandle.of(profileId), /* flags= */ 0);
- return profileContext.getSystemService(DevicePolicyManager.class);
+ private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+ final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ /* value= */ true,
+ parentUserHandle);
}
@Override
@@ -16313,15 +16334,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
disallowAddUser();
-
- final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
- .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
@@ -16420,4 +16432,45 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.setStrings(callerPackage)
.write();
}
+
+ @Override
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int numOfProfiles = profiles.size();
+ if (numOfProfiles <= 1) {
+ return;
+ }
+
+ final String managedProvisioningPackageName = getManagedProvisioningPackage(
+ mContext);
+ // Removes cross profile intent filters from the parent to all the profiles.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, mContext.getOpPackageName());
+ // Setting and resetting default cross profile intent filters was previously handled
+ // by Managed Provisioning. For backwards compatibility, clear any intent filters
+ // that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, managedProvisioningPackageName);
+
+ // For each profile reset cross profile intent filters
+ for (int i = 0; i < numOfProfiles; i++) {
+ UserInfo profile = profiles.get(i);
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, mContext.getOpPackageName());
+ // Clear any intent filters that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, managedProvisioningPackageName);
+
+ mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ });
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index fc1d83158801..222c987d906f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -18,13 +18,17 @@ package com.android.server.devicepolicy;
import android.app.admin.DevicePolicyManager;
import android.os.ShellCommand;
+import com.android.server.devicepolicy.Owners.OwnerDto;
+
import java.io.PrintWriter;
+import java.util.List;
import java.util.Objects;
final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
+ private static final String CMD_LIST_OWNERS = "list-owners";
private final DevicePolicyManagerService mService;
@@ -51,6 +55,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return runIsSafeOperation(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
+ case CMD_LIST_OWNERS:
+ return runListOwners(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -76,6 +82,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
+ pw.printf(" %s\n", CMD_LIST_OWNERS);
+ pw.printf(" Lists the device / profile owners per user \n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -97,4 +105,36 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
+
+ private int runListOwners(PrintWriter pw) {
+ List<OwnerDto> owners = mService.listAllOwners();
+ if (owners.isEmpty()) {
+ pw.println("none");
+ return 0;
+ }
+ int size = owners.size();
+ if (size == 1) {
+ pw.println("1 owner:");
+ } else {
+ pw.printf("%d owners:\n", size);
+ }
+
+ for (int i = 0; i < size; i++) {
+ OwnerDto owner = owners.get(i);
+ pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
+ if (owner.isDeviceOwner) {
+ pw.print(",DeviceOwner");
+ }
+ if (owner.isProfileOwner) {
+ pw.print(",ProfileOwner");
+ }
+ if (owner.isAffiliated) {
+ pw.print(",Affiliated");
+ }
+ pw.println();
+ }
+
+ return 0;
+ }
+
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 809afe01da2d..1e70d59a5fd5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManagerInternal;
import android.app.admin.SystemUpdateInfo;
@@ -57,6 +58,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -433,6 +435,23 @@ class Owners {
}
}
+ List<OwnerDto> listAllOwners() {
+ List<OwnerDto> owners = new ArrayList<>();
+ synchronized (mLock) {
+ if (mDeviceOwner != null) {
+ owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
+ /* isDeviceOwner= */ true));
+ }
+ for (int i = 0; i < mProfileOwners.size(); i++) {
+ int userId = mProfileOwners.keyAt(i);
+ OwnerInfo info = mProfileOwners.valueAt(i);
+ owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+ }
+ }
+ return owners;
+ }
+
+
SystemUpdatePolicy getSystemUpdatePolicy() {
synchronized (mLock) {
return mSystemUpdatePolicy;
@@ -1076,6 +1095,24 @@ class Owners {
}
}
+ /**
+ * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+ */
+ static final class OwnerDto {
+ public final @UserIdInt int userId;
+ public final ComponentName admin;
+ public final boolean isDeviceOwner;
+ public final boolean isProfileOwner;
+ public boolean isAffiliated;
+
+ private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
+ this.userId = userId;
+ this.admin = Objects.requireNonNull(admin, "admin must not be null");
+ this.isDeviceOwner = isDeviceOwner;
+ this.isProfileOwner = !isDeviceOwner;
+ }
+ }
+
public void dump(IndentingPrintWriter pw) {
boolean needBlank = false;
if (mDeviceOwner != null) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index e978ed4000e0..7534c7c40a3d 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@ cc_defaults {
static_libs: [
"libbase",
"libext2_uuid",
- "libdataloader_aidl-unstable-cpp",
- "libincremental_aidl-unstable-cpp",
- "libincremental_manager_aidl-unstable-cpp",
+ "libdataloader_aidl-cpp",
+ "libincremental_aidl-cpp",
+ "libincremental_manager_aidl-cpp",
"libprotobuf-cpp-lite",
"service.incremental.proto",
"libutils",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 50cb00f1887f..7a4c611c57b0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -147,6 +147,7 @@ import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
import com.android.server.pm.BackgroundDexOptService;
@@ -207,12 +208,15 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
+import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
@@ -481,6 +485,50 @@ public final class SystemServer implements Dumpable {
private static native void fdtrackAbort();
+ private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/");
+ private static final int MAX_HEAP_DUMPS = 2;
+
+ /**
+ * Dump system_server's heap.
+ *
+ * For privacy reasons, these aren't automatically pulled into bugreports:
+ * they must be manually pulled by the user.
+ */
+ private static void dumpHprof() {
+ // hprof dumps are rather large, so ensure we don't fill the disk by generating
+ // hundreds of these that will live forever.
+ TreeSet<File> existingTombstones = new TreeSet<>();
+ for (File file : HEAP_DUMP_PATH.listFiles()) {
+ if (!file.isFile()) {
+ continue;
+ }
+ if (!file.getName().startsWith("fdtrack-")) {
+ continue;
+ }
+ existingTombstones.add(file);
+ }
+ if (existingTombstones.size() >= MAX_HEAP_DUMPS) {
+ for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) {
+ // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place.
+ existingTombstones.pollLast();
+ }
+ for (File file : existingTombstones) {
+ if (!file.delete()) {
+ Slog.w("System", "Failed to clean up hprof " + file);
+ }
+ }
+ }
+
+ try {
+ String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+ String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
+ Debug.dumpHprofData(filename);
+ } catch (IOException ex) {
+ Slog.e("System", "Failed to dump fdtrack hprof");
+ ex.printStackTrace();
+ }
+ }
+
/**
* Spawn a thread that monitors for fd leaks.
*/
@@ -505,6 +553,7 @@ public final class SystemServer implements Dumpable {
enabled = true;
} else if (maxFd > abortThreshold) {
Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+ dumpHprof();
fdtrackAbort();
}
@@ -1217,6 +1266,11 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Tracks native tombstones.
+ t.traceBegin("StartNativeTombstoneManagerService");
+ mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+ t.traceEnd();
+
// Service to capture bugreports.
t.traceBegin("StartBugreportManagerService");
mSystemServiceManager.startService(BugreportManagerService.class);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 6daa381f526e..7a0cb8e5dead 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,9 +59,9 @@ android_test {
},
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.hardware.tv.cec-V1.0-java",
- "android.hardware.vibrator-java",
+ "android.hardware.vibrator-V1-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
@@ -88,7 +88,7 @@ android_test {
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
dxflags: ["--multi-dex"],
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853ccdd55b..7597cbf322f5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testDisallowSharingIntoProfileSetRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileClearRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileUnchanged() {
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
}
- private void verifyDataSharingChangedBroadcast() {
+ private void verifyDataSharingAppliedBroadcast() {
Intent expectedIntent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- expectedIntent.setPackage("com.android.managedprovisioning");
- expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(expectedIntent),
- MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE));
}
@Test
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 54da6436ad89..1a2266139405 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -19,10 +19,14 @@ package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
+import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -33,6 +37,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -61,87 +66,69 @@ public final class DeviceStateManagerServiceTest {
}
@Test
- public void requestStateChange() {
+ public void baseStateChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_pendingState() {
+ public void baseStateChanged_withStatePendingPolicyCallback() {
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), 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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_unsupportedState() {
- mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+ public void baseStateChanged_unsupportedState() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+ });
+
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_invalidState() {
+ public void baseStateChanged_invalidState() {
assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifyRequestState(INVALID_DEVICE_STATE);
+ mProvider.setState(INVALID_DEVICE_STATE);
});
- }
-
- @Test
- public void requestOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- OTHER_DEVICE_STATE.getIdentifier());
-
- // Committed state is set back to the requested state once the override is cleared.
- mService.clearOverrideState();
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- DEFAULT_DEVICE_STATE.getIdentifier());
- }
- @Test
- public void requestOverrideState_unsupportedState() {
- mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
- // Committed state remains the same as the override state is unsupported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@@ -150,7 +137,7 @@ public final class DeviceStateManagerServiceTest {
public void supportedStatesChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
@@ -158,46 +145,27 @@ public final class DeviceStateManagerServiceTest {
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
}
@Test
- public void supportedStatesChanged_unsupportedRequestedState() {
+ public void supportedStatesChanged_unsupportedBaseState() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
// The current requested state is cleared because it is no longer supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState(), Optional.empty());
+ assertEquals(mService.getBaseState(), Optional.empty());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
- }
-
- @Test
- public void supportedStatesChanged_unsupportedOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- OTHER_DEVICE_STATE.getIdentifier());
-
- mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
-
- // 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.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
}
@Test
@@ -205,17 +173,17 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertNotNull(callback.getLastNotifiedValue());
assertEquals(callback.getLastNotifiedValue().intValue(),
OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(callback.getLastNotifiedValue().intValue(),
DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ 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(),
@@ -237,6 +205,148 @@ public final class DeviceStateManagerServiceTest {
DEFAULT_DEVICE_STATE.getIdentifier());
}
+ @Test
+ public void getSupportedDeviceStates() throws RemoteException {
+ final int[] expectedStates = new int[] { 0, 1 };
+ assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
+ }
+
+ @Test
+ public void requestState() 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, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ 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.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+
+ mService.getBinderService().cancelRequest(token);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ 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);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_flagCancelWhenBaseChanges() 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, OTHER_DEVICE_STATE.getIdentifier(),
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ 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.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+
+ // Request is canceled because the base state changed.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ 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);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_becomesUnsupported() 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, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ 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.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+
+ // Request is canceled because the state is no longer supported.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // 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);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
+ }
+
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
private final DeviceStateProvider mProvider;
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -306,23 +416,48 @@ public final class DeviceStateManagerServiceTest {
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
}
- public void notifyRequestState(int identifier) {
+ public void setState(int identifier) {
mListener.onStateChanged(identifier);
}
}
private static final class TestDeviceStateManagerCallback extends
IDeviceStateManagerCallback.Stub {
- Integer mLastNotifiedValue;
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ private Integer mLastNotifiedValue;
+ private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
@Override
public void onDeviceStateChanged(int deviceState) {
mLastNotifiedValue = deviceState;
}
+ @Override
+ public void onRequestActive(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_ACTIVE);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_CANCELED);
+ }
+
@Nullable
Integer getLastNotifiedValue() {
return mLastNotifiedValue;
}
+
+ int getLastNotifiedStatus(IBinder requestToken) {
+ return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
+ }
}
}
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 df19aeb13707..58ba90726b80 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@ public class NetworkPolicyManagerServiceTest {
}
/**
- * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
* conditions.
*/
@Test
- public void testIsUidNetworkingBlocked() {
+ public void testCheckUidNetworkingBlocked() {
final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
// Metered network. Data saver on.
@@ -1877,17 +1877,16 @@ public class NetworkPolicyManagerServiceTest {
private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
final boolean expectedResult = pair.first;
final int rule = pair.second;
assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
- expectedResult,
- npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+ metered, backgroundRestricted));
assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
- npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+ backgroundRestricted));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 000000000000..764c504eeede
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+ @Test
+ public void testIsEmpty() {
+ assertThat(BundleUtils.isEmpty(null)).isTrue();
+ assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+ assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+ }
+
+ @Test
+ public void testClone() {
+ Bundle in = new Bundle();
+ Bundle out = BundleUtils.clone(in);
+ assertThat(in).isNotSameInstanceAs(out);
+ assertRestrictions(out, new Bundle());
+
+ out = BundleUtils.clone(null);
+ assertThat(out).isNotNull();
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 9ba096766be2..7b9a00d582be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -71,12 +71,12 @@ public class IncrementalStatesTest {
@Before
public void setUp() {
mIncrementalStates = new IncrementalStates();
- assertFalse(mIncrementalStates.isStartable());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable());
mIncrementalStates.setCallback(mCallback);
mIncrementalStates.onCommit(true);
// Test that package is now startable and loading
- assertTrue(mIncrementalStates.isStartable());
- assertTrue(mIncrementalStates.isLoading());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mUnstartableCalled.close();
mFullyLoadedCalled.close();
}
@@ -90,7 +90,7 @@ public class IncrementalStatesTest {
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
}
@@ -104,7 +104,7 @@ public class IncrementalStatesTest {
IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -116,7 +116,7 @@ public class IncrementalStatesTest {
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -130,7 +130,7 @@ public class IncrementalStatesTest {
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -145,12 +145,12 @@ public class IncrementalStatesTest {
mIncrementalStates.setProgress(1.0f);
// Test that package is now fully loaded
assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isLoading());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -159,6 +159,6 @@ public class IncrementalStatesTest {
@Test
public void testStartableTransition_AppCrashOrAnr() {
mIncrementalStates.onCrashOrAnr();
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68de7a0..cd98d44075ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@ public class UserManagerServiceUserTypeTest {
@Test
public void testUserTypeBuilder_createUserType() {
final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+ final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+ final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+ final List<DefaultCrossProfileIntentFilter> filters = List.of(
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(true)
.setMaxAllowed(21)
- .setBaseType(FLAG_FULL)
+ .setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
.setBadgeLabels(23, 24, 25)
.setBadgeColors(26, 27)
@@ -86,20 +93,45 @@ public class UserManagerServiceUserTypeTest {
.setLabel(31)
.setMaxAllowedPerParent(32)
.setDefaultRestrictions(restrictions)
+ .setDefaultSystemSettings(systemSettings)
+ .setDefaultSecureSettings(secureSettings)
+ .setDefaultCrossProfileIntentFilters(filters)
.createUserTypeDetails();
assertEquals("a.name", type.getName());
assertTrue(type.isEnabled());
assertEquals(21, type.getMaxAllowed());
- assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+ assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
assertEquals(28, type.getIconBadge());
assertEquals(29, type.getBadgePlain());
assertEquals(30, type.getBadgeNoBackground());
assertEquals(31, type.getLabel());
assertEquals(32, type.getMaxAllowedPerParent());
+
assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
assertNotSame(restrictions, type.getDefaultRestrictions());
+ assertNotSame(systemSettings, type.getDefaultSystemSettings());
+ assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+ for (String key : systemSettings.keySet()) {
+ assertEquals(
+ systemSettings.getString(key),
+ type.getDefaultSystemSettings().getString(key));
+ }
+
+ assertNotSame(secureSettings, type.getDefaultSecureSettings());
+ assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+ for (String key : secureSettings.keySet()) {
+ assertEquals(
+ secureSettings.getString(key),
+ type.getDefaultSecureSettings().getString(key));
+ }
+
+ assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+ assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+ for (int i = 0; i < filters.size(); i++) {
+ assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+ }
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@ public class UserManagerServiceUserTypeTest {
assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
assertEquals(Resources.ID_NULL, type.getLabel());
assertTrue(type.getDefaultRestrictions().isEmpty());
+ assertTrue(type.getDefaultSystemSettings().isEmpty());
+ assertTrue(type.getDefaultSecureSettings().isEmpty());
+ assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
assertFalse(type.hasBadge());
}
@@ -416,4 +451,13 @@ public class UserManagerServiceUserTypeTest {
}
return bundle;
}
+
+ /** Creates a Bundle of the given settings keys and puts true for the value. */
+ private static Bundle makeSettingsBundle(String ... settings) {
+ final Bundle bundle = new Bundle();
+ for (String setting : settings) {
+ bundle.putBoolean(setting, true);
+ }
+ return bundle;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a959d83..ddf0cd0e9235 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@ public class UserRestrictionsUtilsTest extends AndroidTestCase {
assertSame(in, UserRestrictionsUtils.nonNull(in));
}
- public void testIsEmpty() {
- assertTrue(UserRestrictionsUtils.isEmpty(null));
- assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
- assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
- }
-
- public void testClone() {
- Bundle in = new Bundle();
- Bundle out = UserRestrictionsUtils.clone(in);
- assertNotSame(in, out);
- assertRestrictions(out, new Bundle());
-
- out = UserRestrictionsUtils.clone(null);
- assertNotNull(out);
- out.putBoolean("a", true); // Should not be Bundle.EMPTY.
- }
-
public void testMerge() {
Bundle a = newRestrictions("a", "d");
Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index e5646db7731f..1dd42127ec06 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -60,6 +60,6 @@ android_test {
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cf977b4a18db..ddf2844012e0 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -51,7 +51,7 @@ android_test {
],
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.test.mock",
"android.test.base",
"android.test.runner",
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 42b080e69211..93851109200b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@ public class ActivityRecordTests extends WindowTestsBase {
new RemoteAnimationAdapter(new Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449eddb0..71f19148d616 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -70,8 +71,10 @@ public class AppChangeTransitionTests extends WindowTestsBase {
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e36098d84e..83aca5e2d482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@ public class AppTransitionTests extends WindowTestsBase {
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3aefea3b..7714a6cead1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@ public class LetterboxTest {
mColor = Color.GREEN;
assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@Test
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 2efd4b53efcc..15e045c85c29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
adapter.onAnimationCancelled(mMockLeash);
verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -170,7 +176,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -190,7 +196,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testZeroAnimations() {
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_NONE);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -199,7 +205,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -213,15 +219,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_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(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
win.mActivityRecord.removeImmediately();
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
@@ -255,15 +264,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
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(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
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(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_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(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
} finally {
@@ -383,15 +401,18 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_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(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf301b5..cc4d4eaa9e8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // Create landscape freeform display and a freeform app.
+ // create freeform display and a freeform app
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into portrait freeform and then make it a compat app.
+ // Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@ public class SizeCompatTests extends WindowTestsBase {
final int density = mActivity.getConfiguration().densityDpi;
- // Change display configuration to fullscreen.
+ // change display configuration to fullscreen
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
- // Size compat mode is sandboxed at the activity level.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -175,12 +171,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -190,17 +180,9 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
}
@Test
@@ -228,9 +210,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
- // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
- // max aspect ratio.
- assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -238,13 +217,11 @@ public class SizeCompatTests extends WindowTestsBase {
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
- // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@ public class SizeCompatTests extends WindowTestsBase {
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
- // Activity is sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -309,8 +280,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@ public class SizeCompatTests extends WindowTestsBase {
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -349,13 +316,14 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testMoveToDifferentOrientationDisplay() {
+ public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+ final int origWidth = configBounds.width();
+ final int origHeight = configBounds.height();
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@ public class SizeCompatTests extends WindowTestsBase {
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(originalBounds.width(), currentBounds.width());
- assertEquals(originalBounds.height(), currentBounds.height());
+ assertEquals(origWidth, configBounds.width());
+ assertEquals(origHeight, configBounds.height());
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode on the new display.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * originalBounds.height()
- / originalBounds.width()));
+ (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(newDisplayBounds.height(), configBounds.height());
+ assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ configBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(currentBounds.width(), appBounds.width());
- assertEquals(currentBounds.height() - notchHeight, appBounds.height());
- // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(configBounds.width(), appBounds.width());
+ assertEquals(configBounds.height() - notchHeight, appBounds.height());
}
@Test
- public void testFixedOrientationRotateCutoutDisplay() {
+ public void testFixedOrientRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- final int width = 1000;
- setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+ setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
- final float maxAspect = 1.4f;
+ // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
- // Activity is sandboxed, and bounds include the area consumed by the notch.
- assertActivityMaxBoundsSandboxedForLetterbox();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@ public class SizeCompatTests extends WindowTestsBase {
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
-
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspectRatioOrientationChangeOrientation() {
+ public void testFixedAspOrientChangeOrient() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -510,8 +459,6 @@ public class SizeCompatTests extends WindowTestsBase {
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@ public class SizeCompatTests extends WindowTestsBase {
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -659,9 +602,6 @@ public class SizeCompatTests extends WindowTestsBase {
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- // Activity is sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
-
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@ public class SizeCompatTests extends WindowTestsBase {
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -696,8 +635,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -741,30 +676,29 @@ public class SizeCompatTests extends WindowTestsBase {
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ Rect activityBounds = new Rect(mActivity.getBounds());
+
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Activity and task inherit max bounds from TaskDisplayArea.
- assertMaxBoundsInheritDisplayAreaBounds();
+ assertEquals(displayBounds, activityBounds);
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
- assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+ displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ activityBounds = new Rect(mActivity.getBounds());
+ assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
- assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
- / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
}
@Test
@@ -797,17 +731,14 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
- final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertThat(taskBounds.width())
- .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
}
@Test
@@ -847,14 +778,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
- // New activity max bounds are sandboxed due to letterbox.
- assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskBounds);
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(displayBounds.height());
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
- .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed due to size compat.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -913,7 +831,6 @@ public class SizeCompatTests extends WindowTestsBase {
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@ public class SizeCompatTests extends WindowTestsBase {
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@ public class SizeCompatTests extends WindowTestsBase {
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -947,26 +862,20 @@ public class SizeCompatTests extends WindowTestsBase {
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed due to mismatched orientation request.
- assertTaskMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- // Activity max bounds are sandboxed due to unresizable app.
- assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed, as in first case.
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -983,18 +892,12 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
- taskDisplayArea.setBounds(displayAreaBounds);
+ taskDisplayArea.setBounds(0, 0, 2400, 1000);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
- // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
}
@Test
@@ -1142,48 +1045,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.hasSizeCompatBounds());
}
- /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
- private void assertMaxBoundsInheritDisplayAreaBounds() {
- final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- }
-
- /**
- * Asserts task-level letterboxing, so both activity and task max bounds
- * are sandboxed to the letterbox bounds.
- */
- private void assertTaskMaxBoundsSandboxed() {
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- // Task max bounds are sandboxed due to letterbox.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- }
-
- /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForSizeCompat() {
- // Activity max bounds are sandboxed due to size compat mode.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getWindowConfiguration().getBounds());
- // Task inherits max bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
- /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForLetterbox() {
- // Activity is sandboxed due to fixed aspect ratio.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getBounds());
- // Task inherits bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b8d44f605bca..6c722499da4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -152,12 +152,6 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
- public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
- SurfaceControl newParent) {
- return this;
- }
-
- @Override
public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513dd369f..0eb8c8d2e58a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@ public class TaskRecordTests extends WindowTestsBase {
}
@Test
+ public void testNotSaveLaunchingStateForNonLeafTask() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+ leafTask.setHasBeenVisible(true);
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ verify(persister, never()).saveTask(same(task), any());
+ verify(persister).saveTask(same(leafTask), any());
+ }
+
+ @Test
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993df8602..ae85ceb72958 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@ class TestDisplayContent extends DisplayContent {
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
- // Ensure letterbox aspect ratio is not overridden on any device target.
- // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
- // the below method, is set on some device form factors.
- mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
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 df5b48a038f3..99c96bd0de1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -905,8 +906,10 @@ public class WindowContainerTests extends WindowTestsBase {
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
try {
finishedCallback.onAnimationFinished();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9e8f8eaa855c..04dea3f7a2c6 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -163,8 +163,13 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mValid = true;
mSessionComponentName = new ComponentName(service.getPackageName(),
mInfo.getSessionService());
- // TODO : Need to get the hotword detection service from the xml metadata
- mHotwordDetectionComponentName = null;
+ final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
+ if (hotwordDetectionServiceName != null) {
+ mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
+ hotwordDetectionServiceName);
+ } else {
+ mHotwordDetectionComponentName = null;
+ }
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
IntentFilter filter = new IntentFilter();
@@ -388,7 +393,11 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
if (DEBUG) {
Slog.d(TAG, "setHotwordDetectionConfigLocked");
}
-
+ if (mHotwordDetectionComponentName == null) {
+ Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
+ + " name not found");
+ return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ }
if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1194d3..1473b7a8873d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@ import android.net.NetworkPolicyManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -150,6 +151,22 @@ public class SubscriptionManager {
public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
"cache_key.telephony.get_slot_index";
+ /** @hide */
+ public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+ /** @hide */
+ public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+ "restoreSimSpecificSettings";
+
+ /**
+ * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+ * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+ * #restoreAllSimSpecificSettings()}.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
private static final int MAX_CACHE_SIZE = 4;
private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@ public class SubscriptionManager {
public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
CONTENT_URI, "wfc_roaming_enabled");
+
+ /**
+ * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+ * settings
+ * <p>
+ * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+ * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "backup_and_restore");
+
+ /**
+ * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+ * SuW.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
/**
* A content {@link Uri} used to receive updates on cross sim enabled user setting.
* <p>
@@ -3455,4 +3494,71 @@ public class SubscriptionManager {
sSlotIndexCache.clear();
sPhoneIdCache.clear();
}
+
+ /**
+ * Called to retrieve SIM-specific settings data to be backed up.
+ *
+ * @return data in byte[] to be backed up.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public byte[] getAllSimSpecificSettingsForBackup() {
+ Bundle bundle = mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+ return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+ }
+
+ /**
+ * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+ * This will try to restore the data that was stored internally when {@link
+ * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+ * End result is SimInfoDB is modified to match any backed up configs for the requested
+ * inserted sim.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param iccId of the sim that a restore is requested for.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ iccId, null);
+ }
+
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+ * data in an internal file. This file will persist on device for device's lifetime and will be
+ * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+ * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+ * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ null, bundle);
+ }
}
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba3771f..d232a507454d 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.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.
@@ -20,22 +20,20 @@ import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Build;
-import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@ public class OemNetworkPreferencesTest {
private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
private static final String TEST_PACKAGE = "com.google.apps.contacts";
- private final List<String> mPackages = new ArrayList<>();
private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
- @Before
- public void beforeEachTestMethod() {
- mPackages.add(TEST_PACKAGE);
+ @Test
+ public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.addNetworkPreference(null, TEST_PREF));
}
@Test
- public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+ public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+ () -> mBuilder.removeNetworkPreference(null));
}
@Test
- public void getNetworkPreferencesReturnsCorrectValue() {
+ public void testGetNetworkPreferenceReturnsCorrectValue() {
final int expectedNumberOfMappings = 1;
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
- final SparseArray<List<String>> networkPreferences =
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertEquals(expectedNumberOfMappings, networkPreferences.size());
- assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
- assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
}
@Test
- public void getNetworkPreferencesReturnsUnmodifiableValue() {
+ public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
final String newPackage = "new.com.google.apps.contacts";
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
- final SparseArray<List<String>> networkPreferences =
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+ () -> networkPreferences.put(newPackage, TEST_PREF));
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).add(newPackage));
+ () -> networkPreferences.remove(TEST_PACKAGE));
+
}
@Test
- public void toStringReturnsCorrectValue() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testToStringReturnsCorrectValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
@@ -99,10 +97,56 @@ public class OemNetworkPreferencesTest {
@Test
public void testOemNetworkPreferencesParcelable() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final OemNetworkPreferences prefs = mBuilder.build();
assertParcelSane(prefs, 1 /* fieldCount */);
}
+
+ @Test
+ public void testAddNetworkPreferenceOverwritesPriorPreference() {
+ final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+ mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+ }
+
+ @Test
+ public void testRemoveNetworkPreferenceRemovesValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+ mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+ }
+
+ @Test
+ public void testConstructorByOemNetworkPreferencesSetsValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ OemNetworkPreferences networkPreference = mBuilder.build();
+
+ final Map<String, Integer> networkPreferences =
+ new OemNetworkPreferences
+ .Builder(networkPreference)
+ .build()
+ .getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c7554f6b79b6..0674138044ff 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1262,22 +1262,28 @@ public class ConnectivityServiceTest {
}
}
- private void updateUidNetworkingBlocked() {
- doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
- i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
- mRestrictBackground)
+ private void mockUidNetworkingBlocked() {
+ doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+ i.getArgument(1) /* metered */, mRestrictBackground)
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+ doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+ inv.getArgument(1) /* uidRules */,
+ inv.getArgument(2) /* isNetworkMetered */,
+ inv.getArgument(3) /* isBackgroundRestricted */)
+ ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+ anyInt(), anyInt(), anyBoolean(), anyBoolean());
}
private void setUidRulesChanged(int uidRules) throws RemoteException {
mUidRules = uidRules;
- updateUidNetworkingBlocked();
mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
}
private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
mRestrictBackground = restrictBackground;
- updateUidNetworkingBlocked();
mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
}
@@ -6809,6 +6815,7 @@ public class ConnectivityServiceTest {
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
@@ -6891,6 +6898,7 @@ public class ConnectivityServiceTest {
public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
+ mockUidNetworkingBlocked();
// No Networkcallbacks invoked before any network is active.
setUidRulesChanged(RULE_REJECT_ALL);
@@ -7160,6 +7168,13 @@ public class ConnectivityServiceTest {
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
+ private void establishLegacyLockdownVpn() throws Exception {
+ // The legacy lockdown VPN only supports userId 0.
+ final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.registerAgent(ranges);
+ mMockVpn.connect(true);
+ }
+
@Test
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
@@ -7254,22 +7269,30 @@ public class ConnectivityServiceTest {
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wlan0");
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ final NetworkCapabilities wifiNc = new NetworkCapabilities();
+ wifiNc.addTransportType(TRANSPORT_WIFI);
+ wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
@@ -7302,16 +7325,20 @@ public class ConnectivityServiceTest {
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
-
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network.
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f4782829cff7..32c6a75bd904 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -213,6 +215,8 @@ public class VpnTest {
when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemServiceName(NotificationManager.class))
@@ -253,12 +257,14 @@ public class VpnTest {
@Test
public void testRestrictedProfilesAreAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
final Vpn vpn = createVpn(primaryUser.id);
- final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
- null, null);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<UidRange> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -267,7 +273,6 @@ public class VpnTest {
@Test
public void testManagedProfilesAreNotAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, managedProfileA);
final Vpn vpn = createVpn(primaryUser.id);
@@ -290,7 +295,6 @@ public class VpnTest {
@Test
public void testUidAllowAndDenylist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -316,7 +320,6 @@ public class VpnTest {
@Test
public void testGetAlwaysAndOnGetLockDown() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
// Default state.
@@ -341,7 +344,6 @@ public class VpnTest {
@Test
public void testLockdownChangingPackage() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -369,7 +371,6 @@ public class VpnTest {
@Test
public void testLockdownAllowlist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -444,7 +445,6 @@ public class VpnTest {
@Test
public void testLockdownRuleRepeatability() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -477,7 +477,6 @@ public class VpnTest {
@Test
public void testLockdownRuleReversibility() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] entireUser = {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -954,7 +953,14 @@ public class VpnTest {
}
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
- setMockedUsers(primaryUser);
+ // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
+ // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
+ // setMockedUsers(primaryUser);
+ final ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(primaryUser);
+ when(mUserManager.getAliveUsers()).thenReturn(users);
+ when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
+ when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
// Dummy egress interface
final LinkProperties lp = new LinkProperties();
@@ -997,14 +1003,12 @@ public class VpnTest {
profile.ipsecIdentifier = "id";
profile.ipsecSecret = "secret";
profile.l2tpSecret = "l2tpsecret";
+
when(mConnectivityManager.getAllNetworks())
.thenReturn(new Network[] { new Network(101) });
+
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
- anyInt(), any(), anyInt())).thenAnswer(invocation -> {
- // The runner has registered an agent and is now ready.
- legacyRunnerReady.open();
- return new Network(102);
- });
+ anyInt(), any(), anyInt())).thenReturn(new Network(102));
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
@@ -1020,14 +1024,20 @@ public class VpnTest {
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800", "mtu", "1270", "mru", "1270" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
// Now wait for the runner to be ready before testing for the route.
- legacyRunnerReady.block(10_000);
- // In this test the expected address is always v4 so /32
+ ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+ lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+ // In this test the expected address is always v4 so /32.
+ // Note that the interface needs to be specified because RouteInfo objects stored in
+ // LinkProperties objects always acquire the LinkProperties' interface.
final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
- RouteInfo.RTN_THROW);
- assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
- + vpn.mConfig.routes,
- vpn.mConfig.routes.contains(expectedRoute));
+ null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+ final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+ assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+ actualRoutes.contains(expectedRoute));
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
@@ -1083,6 +1093,11 @@ public class VpnTest {
}
@Override
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return null;
+ }
+
+ @Override
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1144,6 +1159,10 @@ public class VpnTest {
doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(asUserContext);
+ when(asUserContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
+ when(asUserContext.getSystemService(UserManager.class))
+ .thenReturn(mUserManager);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
@@ -1179,11 +1198,6 @@ public class VpnTest {
final int id = (int) invocation.getArguments()[0];
return userMap.get(id);
}).when(mUserManager).getUserInfo(anyInt());
-
- doAnswer(invocation -> {
- final int id = (int) invocation.getArguments()[0];
- return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
- }).when(mUserManager).canHaveRestrictedProfile();
}
/**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a7d0860210b4..a07bce34c737 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.InsetsSourceTest",
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
+ "android.view.RoundedCornerTest",
+ "android.view.RoundedCornersTest",
"android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest",
"android.app.WindowContextTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a15912b6b4..3e659d0bc128 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@ public class VcnGatewayConnectionConfigTest {
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
new VcnGatewayConnectionConfig.Builder()
.setRetryInterval(RETRY_INTERVALS_MS)
.setMaxMtu(MAX_MTU);
- for (int caps : EXPOSED_CAPS) {
+ for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e32e1e831f83..485964487fda 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@ public class VcnManagementServiceTest {
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+ ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@ public class VcnManagementServiceTest {
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
- }).when(mMockDeps).newVcn(any(), any(), any(), any());
+ }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
@@ -307,7 +311,7 @@ public class VcnManagementServiceTest {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
- .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
+ .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@Test
@@ -485,7 +489,8 @@ public class VcnManagementServiceTest {
eq(mVcnContext),
eq(TEST_UUID_2),
eq(TEST_VCN_CONFIG),
- eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
+ eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+ any());
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -634,4 +639,25 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
+
+ @Test
+ public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_1),
+ eq(TEST_VCN_CONFIG),
+ eq(snapshot),
+ mSafemodeCallbackCaptor.capture());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+ safemodeCallback.onEnteredSafemode();
+
+ assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index fbaae6f534a9..8643d8a2ea8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -45,7 +45,12 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index df1341cce20f..333b5b990dde 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -43,6 +43,7 @@ import android.os.test.TestLooper;
import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@@ -80,6 +81,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@@ -94,6 +96,7 @@ public class VcnGatewayConnectionTestBase {
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
@@ -123,7 +126,12 @@ public class VcnGatewayConnectionTestBase {
mGatewayConnection =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
}
protected IpSecTransform makeDummyIpSecTransform() throws Exception {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 0c1df763a08e..66cbf84619ab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,22 +16,27 @@
package com.android.server.vcn;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
@@ -51,9 +56,13 @@ public class VcnTest {
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
+ private VcnSafemodeCallback mVcnSafemodeCallback;
private Vcn.Dependencies mDeps;
+ private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
private TestLooper mTestLooper;
+ private VcnGatewayConnectionConfig mGatewayConnectionConfig;
private VcnConfig mConfig;
private Vcn mVcn;
@@ -63,6 +72,7 @@ public class VcnTest {
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -76,15 +86,26 @@ public class VcnTest {
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class);
- }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
- mConfig =
- new VcnConfig.Builder(mContext)
- .addGatewayConnectionConfig(
- VcnGatewayConnectionConfigTest.buildTestConfig())
- .build();
+ mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
- mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
+ final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ configBuilder.addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ }
+ configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+ mConfig = configBuilder.build();
+
+ mVcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnSafemodeCallback,
+ mDeps);
}
private NetworkRequestListener verifyAndGetRequestListener() {
@@ -95,23 +116,22 @@ public class VcnTest {
return mNetworkRequestListenerCaptor.getValue();
}
- private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
- final NetworkRequest.Builder builder = new NetworkRequest.Builder();
- for (final int netCapability : networkCapabilities) {
- builder.addCapability(netCapability);
+ private void startVcnGatewayWithCapabilities(
+ NetworkRequestListener requestListener, int... netCapabilities) {
+ final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ for (final int netCapability : netCapabilities) {
+ requestBuilder.addCapability(netCapability);
}
- return builder.build();
+
+ requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+ mTestLooper.dispatchAll();
}
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-
- requestListener.onNetworkRequested(
- getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
- NETWORK_SCORE,
- PROVIDER_ID);
- mTestLooper.dispatchAll();
+ startVcnGatewayWithCapabilities(
+ requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -126,4 +146,38 @@ public class VcnTest {
verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, capability);
+ }
+
+ // 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;
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertEquals(numExpectedGateways, gatewayConnections.size());
+ verify(mDeps, times(numExpectedGateways))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // all Gateways
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onEnteredSafemode();
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+ verify(gatewayConnection).teardownAsynchronously();
+ }
+ verify(mVcnNetworkProvider).unregisterListener(requestListener);
+ verify(mVcnSafemodeCallback).onEnteredSafemode();
+ }
}