summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp8
-rw-r--r--StubLibraries.bp33
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java3
-rw-r--r--cmds/statsd/src/atoms.proto37
-rw-r--r--config/preloaded-classes2
-rw-r--r--core/api/current.txt6
-rw-r--r--core/api/system-current.txt48
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/TaskInfo.java31
-rw-r--r--core/java/android/app/WaitResult.java18
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/pm/LauncherApps.java11
-rw-r--r--core/java/android/net/NetworkCapabilities.java18
-rw-r--r--core/java/android/os/BasicShellCommandHandler.java342
-rw-r--r--core/java/android/os/ShellCommand.java8
-rw-r--r--core/java/android/os/StrictMode.java10
-rw-r--r--core/java/android/provider/DeviceConfig.java14
-rw-r--r--core/java/android/provider/OWNERS1
-rw-r--r--core/java/android/provider/Settings.java21
-rw-r--r--core/java/android/provider/Telephony.java8
-rw-r--r--core/java/android/service/attestation/ImpressionAttestationService.java8
-rw-r--r--core/java/android/uwb/AngleOfArrivalSupport.aidl44
-rw-r--r--core/java/android/uwb/CloseReason.aidl58
-rw-r--r--core/java/android/uwb/IUwbAdapter.aidl170
-rw-r--r--core/java/android/uwb/IUwbAdapterStateCallbacks.aidl32
-rw-r--r--core/java/android/uwb/IUwbRangingCallbacks.aidl73
-rw-r--r--core/java/android/uwb/MeasurementStatus.aidl39
-rw-r--r--core/java/android/uwb/RangingReport.aidl19
-rw-r--r--core/java/android/uwb/SessionHandle.aidl19
-rw-r--r--core/java/android/uwb/SessionHandle.java79
-rw-r--r--core/java/android/uwb/StartFailureReason.aidl52
-rw-r--r--core/java/android/uwb/StateChangeReason.aidl45
-rw-r--r--core/java/android/uwb/UwbAddress.aidl19
-rw-r--r--core/java/android/view/OnReceiveContentListener.java2
-rw-r--r--core/java/android/view/View.java38
-rw-r--r--core/java/android/view/WindowManager.java10
-rw-r--r--core/java/android/view/autofill/AutofillManager.java2
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java8
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/android/widget/TextView.java28
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java37
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java123
-rw-r--r--core/java/com/android/internal/notification/SystemNotificationChannels.java8
-rw-r--r--core/java/com/android/internal/os/BaseCommand.java3
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java36
-rw-r--r--core/java/com/android/internal/os/LooperStats.java18
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java58
-rw-r--r--core/jni/android_os_HwBinder.cpp12
-rw-r--r--core/jni/android_util_Binder.cpp25
-rw-r--r--core/jni/android_util_Binder.h2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp33
-rw-r--r--core/proto/android/stats/tv/tif_enums.proto56
-rw-r--r--core/res/res/values/strings.xml16
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java27
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java14
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java17
-rw-r--r--core/tests/uwbtests/src/android/uwb/SessionHandleTest.java52
-rw-r--r--data/etc/com.android.settings.xml2
-rw-r--r--data/etc/services.core.protolog.json60
-rw-r--r--graphics/java/android/graphics/drawable/TEST_MAPPING24
-rw-r--r--keystore/java/android/security/KeyStoreOperation.java2
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java2
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreProvider.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java4
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java4
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java12
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java19
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt223
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt40
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt58
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java148
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java237
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java10
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java40
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java47
-rw-r--r--media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java46
-rw-r--r--media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java9
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java120
-rw-r--r--media/jni/android_media_tv_Tuner.cpp103
-rw-r--r--media/jni/android_media_tv_Tuner.h5
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml3
-rw-r--r--packages/SystemUI/res/layout/controls_dialog_pin.xml1
-rw-r--r--packages/SystemUI/res/values/styles.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java10
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java106
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java211
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/BatteryService.java26
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java4
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java2
-rw-r--r--services/core/java/com/android/server/LooperStatsService.java13
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java2
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java2
-rw-r--r--services/core/java/com/android/server/adb/AdbShellCommand.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java2
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java1
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java205
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java26
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java207
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java98
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java102
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java47
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java5
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java3
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java9
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java8
-rw-r--r--services/core/java/com/android/server/infra/AbstractMasterSystemService.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodUtils.java2
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java3
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java2
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java23
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java4
-rw-r--r--services/core/java/com/android/server/oemlock/OemLockService.java4
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java376
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/PreferredComponent.java37
-rw-r--r--services/core/java/com/android/server/pm/PreferredIntentResolver.java21
-rw-r--r--services/core/java/com/android/server/pm/Settings.java3
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java13
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java1
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java (renamed from services/core/java/android/os/UserManagerInternal.java)6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java152
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java26
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java13
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java3
-rw-r--r--services/core/java/com/android/server/storage/StorageUserConnection.java2
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java163
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java9
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java186
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java367
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java35
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/DragState.java2
-rw-r--r--services/core/java/com/android/server/wm/ImpressionAttestationController.java356
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java89
-rw-r--r--services/core/java/com/android/server/wm/InputWindowHandleWrapper.java4
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java43
-rw-r--r--services/core/java/com/android/server/wm/KeyguardDisableHandler.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java22
-rw-r--r--services/core/java/com/android/server/wm/Task.java71
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java27
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java2
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java20
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java20
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java2
-rw-r--r--services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java206
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java (renamed from services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java)18
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java95
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java95
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java1
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java1019
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java201
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java87
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java294
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java5
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java34
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java18
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java4
-rw-r--r--telephony/java/android/telephony/SmsManager.java35
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java7
-rw-r--r--telephony/java/android/telephony/data/ApnThrottleStatus.aidl20
-rw-r--r--telephony/java/android/telephony/data/ApnThrottleStatus.java383
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java107
-rw-r--r--telephony/java/android/telephony/data/DataService.java83
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java20
-rw-r--r--telephony/java/android/telephony/data/EpsQos.java105
-rw-r--r--telephony/java/android/telephony/data/IDataService.aidl2
-rw-r--r--telephony/java/android/telephony/data/IDataServiceCallback.aidl1
-rw-r--r--telephony/java/android/telephony/data/IQualifiedNetworksService.aidl2
-rw-r--r--telephony/java/android/telephony/data/NrQos.java112
-rw-r--r--telephony/java/android/telephony/data/Qos.java175
-rw-r--r--telephony/java/android/telephony/data/QosFilter.java373
-rw-r--r--telephony/java/android/telephony/data/QosSession.java125
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java26
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java1
-rw-r--r--tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt5
-rw-r--r--tests/net/integration/util/com/android/server/NetworkAgentWrapper.java6
-rw-r--r--tests/net/integration/util/com/android/server/TestNetIdManager.kt1
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java705
-rw-r--r--wifi/jarjar-rules.txt2
284 files changed, 8684 insertions, 3337 deletions
diff --git a/Android.bp b/Android.bp
index 9b8e01829c6f..aa94d2236084 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,9 +22,9 @@ java_defaults {
],
errorprone: {
javacflags: [
- "-Xep:AndroidFrameworkBinderIdentity:ERROR",
+ // "-Xep:AndroidFrameworkBinderIdentity:ERROR",
"-Xep:AndroidFrameworkCompatChange:ERROR",
- "-Xep:AndroidFrameworkUid:ERROR",
+ // "-Xep:AndroidFrameworkUid:ERROR",
// NOTE: only enable to generate local patchfiles
// "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster",
// "-XepPatchLocation:/tmp/refaster/",
@@ -44,6 +44,7 @@ java_defaults {
"-Xep:AndroidFrameworkEfficientCollections:OFF",
"-Xep:AndroidFrameworkEfficientParcelable:OFF",
"-Xep:AndroidFrameworkEfficientStrings:OFF",
+ "-Xep:AndroidFrameworkEfficientXml:OFF",
],
},
}
@@ -626,6 +627,7 @@ java_defaults {
"av-types-aidl-java",
"mediatranscoding_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
+ "modules-utils-os",
],
}
@@ -1267,7 +1269,6 @@ aidl_mapping {
filegroup {
name: "framework-telephony-common-shared-srcs",
srcs: [
- "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/os/RegistrantList.java",
"core/java/android/os/Registrant.java",
"core/java/android/util/IndentingPrintWriter.java",
@@ -1350,7 +1351,6 @@ filegroup {
name: "framework-wifi-service-shared-srcs",
srcs: [
"core/java/android/net/InterfaceConfiguration.java",
- "core/java/android/os/BasicShellCommandHandler.java",
"core/java/android/util/BackupUtils.java",
"core/java/android/util/Rational.java",
"core/java/com/android/internal/util/FastXmlSerializer.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49a42d7525a5..380839e5c06b 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -70,6 +70,7 @@ stubs_defaults {
"android.hardware.cas-V1.2-java",
"android.hardware.health-V1.0-java-constants",
"android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
@@ -252,21 +253,7 @@ java_library_static {
"framework-wifi.stubs",
"private-stub-annotations-jar",
],
- defaults: [
- "android_defaults_stubs_current",
- "android_stubs_dists_default",
- ],
- dist: {
- dir: "apistubs/android/system",
- },
- dists: [
- {
- // Legacy dist path
- targets: ["sdk", "win_sdk"],
- tag: ".jar",
- dest: "android_system.jar",
- },
- ],
+ defaults: ["android_defaults_stubs_current"],
}
java_library_static {
@@ -285,7 +272,21 @@ java_library_static {
"framework-wifi.stubs.system",
"private-stub-annotations-jar",
],
- defaults: ["android_defaults_stubs_current"],
+ defaults: [
+ "android_defaults_stubs_current",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/system",
+ },
+ dists: [
+ {
+ // Legacy dist path
+ targets: ["sdk", "win_sdk"],
+ tag: ".jar",
+ dest: "android_system.jar",
+ },
+ ],
}
java_library_static {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index d37dfdeaa583..f77f6c642f6a 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -79,7 +79,6 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -106,6 +105,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import com.android.server.blob.BlobMetadata.Committer;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.StorageStatsManagerInternal;
import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ca002ec992e2..fe96952d83f9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -63,7 +63,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.WorkSource;
import android.provider.DeviceConfig;
import android.text.format.DateUtils;
@@ -104,6 +103,7 @@ import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import com.android.server.utils.quota.Categorizer;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 1e7206287566..cc202130ab07 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -20,10 +20,11 @@ import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.UserHandle;
+import com.android.modules.utils.BasicShellCommandHandler;
+
import java.io.PrintWriter;
public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0f60eaebd59f..a9037701fa01 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -60,6 +60,7 @@ import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
import "frameworks/base/core/proto/android/stats/tls/enums.proto";
+import "frameworks/base/core/proto/android/stats/tv/tif_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -510,6 +511,7 @@ message Atom {
MediaPlaybackTrackChanged media_playback_track_changed = 324;
WifiScanReported wifi_scan_reported = 325 [(module) = "wifi"];
WifiPnoScanReported wifi_pno_scan_reported = 326 [(module) = "wifi"];
+ TifTuneStateChanged tif_tune_changed = 327 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -3766,6 +3768,7 @@ message AppStartOccurred {
WARM = 1;
HOT = 2;
COLD = 3;
+ RELAUNCH = 4;
}
// The transition type.
optional TransitionType type = 3;
@@ -3827,6 +3830,7 @@ message AppStartCanceled {
WARM = 1;
HOT = 2;
COLD = 3;
+ RELAUNCH = 4;
}
// The transition type.
optional TransitionType type = 3;
@@ -10352,6 +10356,39 @@ message CellBroadcastMessageError {
}
/**
+ * Logs when a TV Input Service Session changes tune state
+ * This is atom ID 327.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
+ */
+message TifTuneStateChanged {
+
+ // Tv Input Service uid, TV Player uid
+ repeated AttributionNode attribution_node = 1 [
+ (state_field_option).primary_field_first_uid = true
+ ];
+ optional android.stats.tv.TifTuneState state = 2 [
+ (state_field_option).exclusive_state = true,
+ (state_field_option).default_state_value = 0,
+ (state_field_option).nested = false
+ ];
+ // This a globally unique 128 bit random number created by TvInputManagerService when
+ // android.media.tv.TvInputManager#createSession is called.
+ // It is has no device or user association.
+ // See android.media.tv.TvInputService.onCreateSession(java.lang.String, java.lang.String)
+ // WARNING: Any changes to this field should be carefully reviewed for privacy.
+ // Inspect the code at
+ // framework/base/cmds/statsd/src/atoms.proto
+ // TifTuneState
+ // frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
+ // logTuneStateChanged
+ // BinderService.createSession
+ // SessionState.sessionId
+ optional string tif_session_id = 3 [(state_field_option).primary_field = true];
+}
+
+/**
* Logs when a tune occurs through device's Frontend.
* This is atom ID 276.
*
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 822b40be811a..bbdb5e9be99e 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -5575,7 +5575,6 @@ android.os.AsyncTask
android.os.BadParcelableException
android.os.BaseBundle$NoImagePreloadHolder
android.os.BaseBundle
-android.os.BasicShellCommandHandler
android.os.BatteryManager
android.os.BatteryManagerInternal
android.os.BatteryProperty$1
@@ -11756,6 +11755,7 @@ com.android.net.module.annotation.VisibleForTesting$Visibility
com.android.net.module.annotation.VisibleForTesting
com.android.net.module.annotation.WorkerThread
com.android.net.module.util.InetAddressUtils
+com.android.modules.utils.BasicShellCommandHandler
com.android.okhttp.Address
com.android.okhttp.AndroidShimResponseCache
com.android.okhttp.Authenticator
diff --git a/core/api/current.txt b/core/api/current.txt
index f665512070f5..821717a195a1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10158,6 +10158,7 @@ package android.content {
method public abstract void grantUriPermission(String, android.net.Uri, int);
method public abstract boolean isDeviceProtectedStorage();
method public boolean isRestricted();
+ method public static boolean isUiContext(@NonNull android.content.Context);
method public abstract boolean moveDatabaseFrom(android.content.Context, String);
method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String);
method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]);
@@ -35793,6 +35794,7 @@ package android.os {
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot();
+ method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
@@ -46595,6 +46597,8 @@ package android.telephony {
field public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504; // 0x1f8
field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f
field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
+ field public static final int RESULT_RIL_ACCESS_BARRED = 122; // 0x7a
+ field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b
field public static final int RESULT_RIL_CANCELLED = 119; // 0x77
field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d
field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71
@@ -46613,6 +46617,7 @@ package android.telephony {
field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64
field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72
field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a
+ field public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; // 0x79
field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78
field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65
field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c
@@ -52924,6 +52929,7 @@ package android.view {
method public boolean performHapticFeedback(int, int);
method public boolean performLongClick();
method public boolean performLongClick(float, float);
+ method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
method public void playSoundEffect(int);
method public boolean post(Runnable);
method public boolean postDelayed(Runnable, long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6cf93fc1a403..a56b39675c36 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2066,6 +2066,10 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
}
+ public static class LauncherApps.ShortcutQuery {
+ field public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
+ }
+
public class PackageInstaller {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -5027,14 +5031,15 @@ package android.media.tv.tuner {
method public void clearResourceLostListener();
method public void close();
method public int connectCiCam(int);
+ method public int connectFrontendToCiCam(int);
method public int disconnectCiCam();
+ method public int disconnectFrontendToCiCam(int);
method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
method public long getAvSyncTime(int);
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getFrontendInfoList();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
- method public int linkFrontendToCiCam(int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -5048,7 +5053,6 @@ package android.media.tv.tuner {
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
- method public int unlinkFrontendToCiCam(int);
method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
@@ -5249,7 +5253,7 @@ package android.media.tv.tuner.filter {
public class Filter implements java.lang.AutoCloseable {
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
- method public int configureScramblingStatusEvent(int);
+ method public int configureMonitorEvent(int);
method public int flush();
method public int getId();
method public long getId64Bit();
@@ -5257,6 +5261,8 @@ package android.media.tv.tuner.filter {
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int start();
method public int stop();
+ field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2
+ field public static final int MONITOR_EVENT_SCRAMBLING_STATUS = 1; // 0x1
field public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED = 2; // 0x2
field public static final int SCRAMBLING_STATUS_SCRAMBLED = 4; // 0x4
field public static final int SCRAMBLING_STATUS_UNKNOWN = 1; // 0x1
@@ -5303,6 +5309,10 @@ package android.media.tv.tuner.filter {
ctor public FilterEvent();
}
+ public final class IpCidChangeEvent extends android.media.tv.tuner.filter.FilterEvent {
+ method public int getIpCid();
+ }
+
public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
@@ -11082,6 +11092,35 @@ package android.telephony.data {
field public static final String TYPE_XCAP_STRING = "xcap";
}
+ public final class ApnThrottleStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getApnType();
+ method public int getRetryType();
+ method public int getSlotIndex();
+ method public long getThrottleExpiryTimeMillis();
+ method public int getThrottleType();
+ method public int getTransportType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ApnThrottleStatus> CREATOR;
+ field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3
+ field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2
+ field public static final int RETRY_TYPE_NONE = 1; // 0x1
+ field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2
+ field public static final int THROTTLE_TYPE_NONE = 1; // 0x1
+ }
+
+ public static final class ApnThrottleStatus.Builder {
+ ctor public ApnThrottleStatus.Builder();
+ method @NonNull public android.telephony.data.ApnThrottleStatus build();
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setApnType(int);
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setNoThrottle();
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setRetryType(int);
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int);
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long);
+ method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int);
+ field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL
+ }
+
public final class DataCallResponse implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -11199,6 +11238,7 @@ package android.telephony.data {
method public abstract void close();
method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback);
method public final int getSlotIndex();
+ method public final void notifyApnUnthrottled(@NonNull String);
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -11209,6 +11249,7 @@ package android.telephony.data {
}
public class DataServiceCallback {
+ method public void onApnUnthrottled(@NonNull String);
method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
method public void onDeactivateDataCallComplete(int);
method public void onHandoverCancelled(int);
@@ -11234,6 +11275,7 @@ package android.telephony.data {
ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
method public abstract void close();
method public final int getSlotIndex();
+ method public void reportApnThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ApnThrottleStatus>);
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f695adf3e01c..fda7c203226c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1227,7 +1227,6 @@ package android.os {
}
public static final class StrictMode.VmPolicy.Builder {
- method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75f7ceca6450..e40247ba2d9a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -67,6 +67,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -5348,8 +5349,11 @@ public class Notification implements Parcelable
big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
big.setTextViewText(R.id.notification_material_reply_text_3, null);
+ final boolean snoozeEnabled = mContext.getContentResolver() != null
+ && (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
- R.dimen.notification_content_margin);
+ snoozeEnabled ? 0 : R.dimen.notification_content_margin);
}
private RemoteViews applyStandardTemplateWithActions(int layoutId, int viewType,
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 7fdf06f7233c..da8ac96ef40b 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -36,6 +36,7 @@ import android.util.Log;
import android.window.WindowContainerToken;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Stores information about a particular Task.
@@ -288,6 +289,36 @@ public class TaskInfo {
}
/**
+ * Returns {@code true} if parameters that are important for task organizers have changed
+ * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners
+ * about that.
+ * @hide
+ */
+ public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) {
+ if (that == null) {
+ return false;
+ }
+ return topActivityType == that.topActivityType
+ && isResizeable == that.isResizeable
+ && Objects.equals(positionInParent, that.positionInParent)
+ && equalsLetterboxParams(that)
+ && pictureInPictureParams == that.pictureInPictureParams
+ && getWindowingMode() == that.getWindowingMode()
+ && Objects.equals(taskDescription, that.taskDescription);
+ }
+
+ private boolean equalsLetterboxParams(TaskInfo that) {
+ return Objects.equals(letterboxActivityBounds, that.letterboxActivityBounds)
+ && Objects.equals(
+ getConfiguration().windowConfiguration.getBounds(),
+ that.getConfiguration().windowConfiguration.getBounds())
+ && Objects.equals(
+ getConfiguration().windowConfiguration.getMaxBounds(),
+ that.getConfiguration().windowConfiguration.getMaxBounds())
+ && Objects.equals(parentBounds, that.parentBounds);
+ }
+
+ /**
* Reads the TaskInfo from a parcel.
*/
void readFromParcel(Parcel source) {
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index d65be9bded4f..77891e0fd007 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -40,14 +40,21 @@ public class WaitResult implements Parcelable {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+ LAUNCH_STATE_UNKNOWN,
LAUNCH_STATE_COLD,
LAUNCH_STATE_WARM,
- LAUNCH_STATE_HOT
+ LAUNCH_STATE_HOT,
+ LAUNCH_STATE_RELAUNCH
})
public @interface LaunchState {
}
/**
+ * Not considered as a launch event, e.g. the activity is already on top.
+ */
+ public static final int LAUNCH_STATE_UNKNOWN = 0;
+
+ /**
* Cold launch sequence: a new process has started.
*/
public static final int LAUNCH_STATE_COLD = 1;
@@ -62,6 +69,13 @@ public class WaitResult implements Parcelable {
*/
public static final int LAUNCH_STATE_HOT = 3;
+ /**
+ * Relaunch launch sequence: process reused, but activity has to be destroyed and created.
+ * E.g. the current device configuration is different from the background activity that will be
+ * brought to foreground, and the activity doesn't declare to handle the change.
+ */
+ public static final int LAUNCH_STATE_RELAUNCH = 4;
+
public static final int INVALID_DELAY = -1;
public int result;
public boolean timeout;
@@ -124,6 +138,8 @@ public class WaitResult implements Parcelable {
return "WARM";
case LAUNCH_STATE_HOT:
return "HOT";
+ case LAUNCH_STATE_RELAUNCH:
+ return "RELAUNCH";
default:
return "UNKNOWN (" + type + ")";
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 37e0a44bdd95..6a3f6b4034ff 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -63,6 +63,7 @@ import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
+import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -6285,4 +6286,25 @@ public abstract class Context {
public boolean isUiContext() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
+
+ /**
+ * Returns {@code true} if the context is a UI context which can access UI components such as
+ * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
+ * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
+ * contexts throws {@link android.os.strictmode.Violation} if
+ * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
+ * <p>
+ * Examples of UI contexts are
+ * an {@link android.app.Activity Activity}, a context created from
+ * {@link #createWindowContext(int, Bundle)} or
+ * {@link android.inputmethodservice.InputMethodService InputMethodService}
+ * </p>
+ *
+ * @see #getDisplay()
+ * @see #getSystemService(String)
+ * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+ */
+ public static boolean isUiContext(@NonNull Context context) {
+ return context.isUiContext();
+ }
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index c964b4b9bb53..4b7bbbfa9d20 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -433,6 +433,16 @@ public class LauncherApps {
*/
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
+ /**
+ * Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
+ *
+ * <p>The caller must have the system {@code ACCESS_SHORTCUTS} permission.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_GET_PERSONS_DATA = 1 << 11;
+
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_MATCH_DYNAMIC,
@@ -440,6 +450,7 @@ public class LauncherApps {
FLAG_MATCH_MANIFEST,
FLAG_MATCH_CACHED,
FLAG_GET_KEY_FIELDS_ONLY,
+ FLAG_GET_PERSONS_DATA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface QueryFlags {}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f806b565b127..7c2636c52be8 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1802,21 +1802,27 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (mAdministratorUids.length == 0) {
- sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
+ if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
+ }
+
+ if (mRequestorUid != Process.INVALID_UID) {
+ sb.append(" RequestorUid: ").append(mRequestorUid);
+ }
+
+ if (mRequestorPackageName != null) {
+ sb.append(" RequestorPkg: ").append(mRequestorPackageName);
}
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
+
if (mPrivateDnsBroken) {
- sb.append(" Private DNS is broken");
+ sb.append(" PrivateDnsBroken");
}
- sb.append(" RequestorUid: ").append(mRequestorUid);
- sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
-
sb.append("]");
return sb.toString();
}
diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java
deleted file mode 100644
index 366da3db0010..000000000000
--- a/core/java/android/os/BasicShellCommandHandler.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.util.Log;
-
-import java.io.BufferedInputStream;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-
-/**
- * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. This is meant to
- * be copied into mainline modules, so this class must not use any hidden APIs.
- *
- * @hide
- */
-public abstract class BasicShellCommandHandler {
- static final String TAG = "ShellCommand";
- static final boolean DEBUG = false;
-
- private Binder mTarget;
- private FileDescriptor mIn;
- private FileDescriptor mOut;
- private FileDescriptor mErr;
- private String[] mArgs;
-
- private String mCmd;
- private int mArgPos;
- private String mCurArgData;
-
- private FileInputStream mFileIn;
- private FileOutputStream mFileOut;
- private FileOutputStream mFileErr;
-
- private PrintWriter mOutPrintWriter;
- private PrintWriter mErrPrintWriter;
- private InputStream mInputStream;
-
- public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, int firstArgPos) {
- mTarget = target;
- mIn = in;
- mOut = out;
- mErr = err;
- mArgs = args;
- mCmd = null;
- mArgPos = firstArgPos;
- mCurArgData = null;
- mFileIn = null;
- mFileOut = null;
- mFileErr = null;
- mOutPrintWriter = null;
- mErrPrintWriter = null;
- mInputStream = null;
- }
-
- public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args) {
- String cmd;
- int start;
- if (args != null && args.length > 0) {
- cmd = args[0];
- start = 1;
- } else {
- cmd = null;
- start = 0;
- }
- init(target, in, out, err, args, start);
- mCmd = cmd;
-
- if (DEBUG) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here);
- Log.d(TAG, "Calling uid=" + Binder.getCallingUid()
- + " pid=" + Binder.getCallingPid());
- }
- int res = -1;
- try {
- res = onCommand(mCmd);
- if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget);
- } catch (Throwable e) {
- // Unlike usual calls, in this case if an exception gets thrown
- // back to us we want to print it back in to the dump data, since
- // that is where the caller expects all interesting information to
- // go.
- PrintWriter eout = getErrPrintWriter();
- eout.println();
- eout.println("Exception occurred while executing '" + mCmd + "':");
- e.printStackTrace(eout);
- } finally {
- if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget);
- if (mOutPrintWriter != null) {
- mOutPrintWriter.flush();
- }
- if (mErrPrintWriter != null) {
- mErrPrintWriter.flush();
- }
- if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget);
- }
- if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget);
- return res;
- }
-
- /**
- * Return the raw FileDescriptor for the output stream.
- */
- public FileDescriptor getOutFileDescriptor() {
- return mOut;
- }
-
- /**
- * Return direct raw access (not buffered) to the command's output data stream.
- */
- public OutputStream getRawOutputStream() {
- if (mFileOut == null) {
- mFileOut = new FileOutputStream(mOut);
- }
- return mFileOut;
- }
-
- /**
- * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}.
- */
- public PrintWriter getOutPrintWriter() {
- if (mOutPrintWriter == null) {
- mOutPrintWriter = new PrintWriter(getRawOutputStream());
- }
- return mOutPrintWriter;
- }
-
- /**
- * Return the raw FileDescriptor for the error stream.
- */
- public FileDescriptor getErrFileDescriptor() {
- return mErr;
- }
-
- /**
- * Return direct raw access (not buffered) to the command's error output data stream.
- */
- public OutputStream getRawErrorStream() {
- if (mFileErr == null) {
- mFileErr = new FileOutputStream(mErr);
- }
- return mFileErr;
- }
-
- /**
- * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}.
- */
- public PrintWriter getErrPrintWriter() {
- if (mErr == null) {
- return getOutPrintWriter();
- }
- if (mErrPrintWriter == null) {
- mErrPrintWriter = new PrintWriter(getRawErrorStream());
- }
- return mErrPrintWriter;
- }
-
- /**
- * Return the raw FileDescriptor for the input stream.
- */
- public FileDescriptor getInFileDescriptor() {
- return mIn;
- }
-
- /**
- * Return direct raw access (not buffered) to the command's input data stream.
- */
- public InputStream getRawInputStream() {
- if (mFileIn == null) {
- mFileIn = new FileInputStream(mIn);
- }
- return mFileIn;
- }
-
- /**
- * Return buffered access to the command's {@link #getRawInputStream()}.
- */
- public InputStream getBufferedInputStream() {
- if (mInputStream == null) {
- mInputStream = new BufferedInputStream(getRawInputStream());
- }
- return mInputStream;
- }
-
- /**
- * Return the next option on the command line -- that is an argument that
- * starts with '-'. If the next argument is not an option, null is returned.
- */
- public String getNextOption() {
- if (mCurArgData != null) {
- String prev = mArgs[mArgPos - 1];
- throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
- }
- if (mArgPos >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mArgPos];
- if (!arg.startsWith("-")) {
- return null;
- }
- mArgPos++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- /**
- * Return the next argument on the command line, whatever it is; if there are
- * no arguments left, return null.
- */
- public String getNextArg() {
- if (mCurArgData != null) {
- String arg = mCurArgData;
- mCurArgData = null;
- return arg;
- } else if (mArgPos < mArgs.length) {
- return mArgs[mArgPos++];
- } else {
- return null;
- }
- }
-
- public String peekNextArg() {
- if (mCurArgData != null) {
- return mCurArgData;
- } else if (mArgPos < mArgs.length) {
- return mArgs[mArgPos];
- } else {
- return null;
- }
- }
-
- /**
- * @return all the remaining arguments in the command without moving the current position.
- */
- public String[] peekRemainingArgs() {
- int remaining = getRemainingArgsCount();
- String[] args = new String[remaining];
- for (int pos = mArgPos; pos < mArgs.length; pos++) {
- args[pos - mArgPos] = mArgs[pos];
- }
- return args;
- }
-
- /**
- * Returns number of arguments that haven't been processed yet.
- */
- public int getRemainingArgsCount() {
- if (mArgPos >= mArgs.length) {
- return 0;
- }
- return mArgs.length - mArgPos;
- }
-
- /**
- * Return the next argument on the command line, whatever it is; if there are
- * no arguments left, throws an IllegalArgumentException to report this to the user.
- */
- public String getNextArgRequired() {
- String arg = getNextArg();
- if (arg == null) {
- String prev = mArgs[mArgPos - 1];
- throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
- }
- return arg;
- }
-
- public int handleDefaultCommands(String cmd) {
- if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
- onHelp();
- } else {
- getOutPrintWriter().println("Unknown command: " + cmd);
- }
- return -1;
- }
-
- public Binder getTarget() {
- return mTarget;
- }
-
- public String[] getAllArgs() {
- return mArgs;
- }
-
- /**
- * Implement parsing and execution of a command. If it isn't a command you understand,
- * call {@link #handleDefaultCommands(String)} and return its result as a last resort.
- * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
- * to process additional command line arguments. Command output can be written to
- * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
- *
- * <p class="caution">Note that no permission checking has been done before entering this
- * function, so you need to be sure to do your own security verification for any commands you
- * are executing. The easiest way to do this is to have the ShellCommand contain
- * only a reference to your service's aidl interface, and do all of your command
- * implementations on top of that -- that way you can rely entirely on your executing security
- * code behind that interface.</p>
- *
- * @param cmd The first command line argument representing the name of the command to execute.
- * @return Return the command result; generally 0 or positive indicates success and
- * negative values indicate error.
- */
- public abstract int onCommand(String cmd);
-
- /**
- * Implement this to print help text about your command to {@link #getOutPrintWriter()}.
- */
- public abstract void onHelp();
-}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 3358ce13ed52..a2173a6d2360 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,15 +19,9 @@ package android.os;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Slog;
-import com.android.internal.util.FastPrintWriter;
+import com.android.modules.utils.BasicShellCommandHandler;
-import java.io.BufferedInputStream;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
/**
* Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index c89adadfbf2d..086180e7ead4 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -85,8 +85,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -1045,22 +1043,22 @@ public final class StrictMode {
/**
* Detect attempts to invoke a method on a {@link Context} that is not suited for such
* operation.
- * <p>An example of this is trying to obtain an instance of visual service (e.g.
+ * <p>An example of this is trying to obtain an instance of UI service (e.g.
* {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
* allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
* therefore can report incorrect metrics or resources.
* @see Context#getDisplay()
* @see Context#getSystemService(String)
- * @hide
*/
- @TestApi
public @NonNull Builder detectIncorrectContextUse() {
return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
}
/**
* Disable detection of incorrect context use.
- * TODO(b/149790106): Fix usages and remove.
+ *
+ * @see #detectIncorrectContextUse()
+ *
* @hide
*/
@TestApi
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 97c9f4bbb4ba..714bcea9c01f 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -457,6 +457,20 @@ public final class DeviceConfig {
*/
public static final String NAMESPACE_CONFIGURATION = "configuration";
+ /**
+ * LatencyTracker properties definitions.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_LATENCY_TRACKER = "latency_tracker";
+
+ /**
+ * InteractionJankMonitor properties definitions.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_INTERACTION_JANK_MONITOR = "interaction_jank_monitor";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index 8b7d6ad851f9..97e015646df0 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,4 +1,5 @@
per-file DeviceConfig.java = svetoslavganov@google.com
per-file DeviceConfig.java = hackbod@google.com
+per-file DeviceConfig.java = schfan@google.com
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3e48168eabe0..884f8ccd5e54 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8412,6 +8412,14 @@ public final class Settings {
"emergency_gesture_sound_enabled";
/**
+ * The default number to call in emergency gesture
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_GESTURE_CALL_NUMBER =
+ "emergency_gesture_call_number";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
@@ -14576,19 +14584,6 @@ public final class Settings {
*/
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
-
- /**
- * LatencyTracker settings.
- *
- * The following strings are supported as keys:
- * <pre>
- * enabled (boolean)
- * sampling_interval (int)
- * </pre>
- *
- * @hide
- */
- public static final String LATENCY_TRACKER = "latency_tracker";
}
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 649c8f353196..83cab0a0d3de 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5169,6 +5169,14 @@ public final class Telephony {
public static final String COLUMN_IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
/**
+ * TelephonyProvider column name for determining if the user has enabled cross SIM calling
+ * for this subscription.
+ *
+ * @hide
+ */
+ public static final String COLUMN_CROSS_SIM_CALLING_ENABLED = "cross_sim_calling_enabled";
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
index 4919f5d8856f..923ab7a65d1b 100644
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ b/core/java/android/service/attestation/ImpressionAttestationService.java
@@ -71,6 +71,14 @@ public abstract class ImpressionAttestationService extends Service {
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.attestation.available_algorithms";
+ /**
+ * The {@link Intent} action that must be declared as handled by a service in its manifest
+ * for the system to recognize it as an impression attestation providing service.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.attestation.ImpressionAttestationService";
+
private ImpressionAttestationServiceWrapper mWrapper;
private Handler mHandler;
diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl
new file mode 100644
index 000000000000..57666ff8bca9
--- /dev/null
+++ b/core/java/android/uwb/AngleOfArrivalSupport.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum AngleOfArrivalSupport {
+ /**
+ * The device does not support angle of arrival
+ */
+ NONE,
+
+ /**
+ * The device supports planar angle of arrival
+ */
+ TWO_DIMENSIONAL,
+
+ /**
+ * The device does supports three dimensional angle of arrival with hemispherical azimuth angles
+ */
+ THREE_DIMENSIONAL_HEMISPHERICAL,
+
+ /**
+ * The device does supports three dimensional angle of arrival with full azimuth angles
+ */
+ THREE_DIMENSIONAL_SPHERICAL,
+}
+
diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/CloseReason.aidl
new file mode 100644
index 000000000000..bef129e2c1c7
--- /dev/null
+++ b/core/java/android/uwb/CloseReason.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum CloseReason {
+ /**
+ * Unknown reason
+ */
+ UNKNOWN,
+
+ /**
+ * A local API call triggered the close, such as a call to
+ * IUwbAdapter.stopRanging.
+ */
+ LOCAL_API,
+
+ /**
+ * The maximum number of sessions has been reached. This error may be generated
+ * for an active session if a higher priority session begins.
+ */
+ MAX_SESSIONS_REACHED,
+
+ /**
+ * The system state has changed resulting in the session ending (e.g. the user
+ * disables UWB, or the user's locale changes and an active channel is no longer
+ * permitted to be used).
+ */
+ SYSTEM_POLICY,
+
+ /**
+ * The remote device has requested to terminate the session
+ */
+ REMOTE_REQUEST,
+
+ /**
+ * The session was closed for a protocol specific reason
+ */
+ PROTOCOL_SPECIFIC,
+}
+
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
new file mode 100644
index 000000000000..d29ed34804f1
--- /dev/null
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.os.PersistableBundle;
+import android.uwb.AngleOfArrivalSupport;
+import android.uwb.IUwbAdapterStateCallbacks;
+import android.uwb.IUwbRangingCallbacks;
+import android.uwb.SessionHandle;
+
+/**
+ * @hide
+ */
+interface IUwbAdapter {
+ /*
+ * Register the callbacks used to notify the framework of events and data
+ *
+ * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
+ * function must be called immediately following registration with the current
+ * state of the UWB adapter.
+ *
+ * @param callbacks callback to provide range and status updates to the framework
+ */
+ void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks);
+
+ /*
+ * Unregister the callbacks used to notify the framework of events and data
+ *
+ * Calling this function with an unregistered callback is a no-op
+ *
+ * @param callbacks callback to unregister
+ */
+ void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
+
+ /**
+ * Returns true if ranging is supported, false otherwise
+ */
+ boolean isRangingSupported();
+
+ /**
+ * Get the angle of arrival supported by this device
+ *
+ * @return the angle of arrival type supported
+ */
+ AngleOfArrivalSupport getAngleOfArrivalSupport();
+
+ /**
+ * Generates a list of the supported 802.15.4z channels
+ *
+ * The list must be prioritized in the order of preferred channel usage.
+ *
+ * The list must only contain channels that are permitted to be used in the
+ * device's current location.
+ *
+ * @return an array of support channels on the device for the current location.
+ */
+ int[] getSupportedChannels();
+
+ /**
+ * Generates a list of the supported 802.15.4z preamble codes
+ *
+ * The list must be prioritized in the order of preferred preamble usage.
+ *
+ * The list must only contain preambles that are permitted to be used in the
+ * device's current location.
+ *
+ * @return an array of supported preambles on the device for the current
+ * location.
+ */
+ int[] getSupportedPreambleCodes();
+
+ /**
+ * Get the accuracy of the ranging timestamps
+ *
+ * @return accuracy of the ranging timestamps in nanoseconds
+ */
+ long getTimestampResolutionNanos();
+
+ /**
+ * Get the supported number of simultaneous ranging sessions
+ *
+ * @return the supported number of simultaneous ranging sessions
+ */
+ int getMaxSimultaneousSessions();
+
+ /**
+ * Get the maximum number of remote devices per session
+ *
+ * @return the maximum number of remote devices supported in a single session
+ */
+ int getMaxRemoteDevicesPerSession();
+
+ /**
+ * Provides the capabilities and features of the device
+ *
+ * @return specification specific capabilities and features of the device
+ */
+ PersistableBundle getSpecificationInfo();
+
+ /**
+ * Request to start a new ranging session
+ *
+ * This function must return before calling IUwbAdapterCallbacks
+ * #onRangingStarted, #onRangingClosed, or #onRangingResult.
+ *
+ * A ranging session does not need to be started before returning.
+ *
+ * IUwbAdapterCallbacks#onRangingStarted must be called within
+ * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
+ * if the ranging session is scheduled to start successfully.
+ *
+ * IUwbAdapterCallbacks#onRangingStartFailed must be called within
+ * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called
+ * if the ranging session fails to be scheduled to start successfully.
+ *
+ * @param rangingCallbacks the callbacks used to deliver ranging information
+ * @param parameters the configuration to use for ranging
+ * @return a SessionHandle used to identify this ranging request
+ */
+ SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks,
+ in PersistableBundle parameters);
+
+ /**
+ * Stop and close ranging for the session associated with the given handle
+ *
+ * Calling with an invalid handle or a handle that has already been closed
+ * is a no-op.
+ *
+ * IUwbAdapterCallbacks#onRangingClosed must be called within
+ * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called.
+ *
+ * @param sessionHandle the session handle to stop ranging for
+ */
+ void closeRanging(in SessionHandle sessionHandle);
+
+ /**
+ * The maximum allowed time to start a ranging session.
+ */
+ const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD
+
+ /**
+ * The maximum allowed time to notify the framework that a session has been
+ * closed.
+ */
+ const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
+
+ /**
+ * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY
+ */
+ const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416;
+
+ /**
+ * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY
+ */
+ const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1;
+}
diff --git a/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl
new file mode 100644
index 000000000000..d928eabae465
--- /dev/null
+++ b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.uwb.StateChangeReason;
+
+/**
+ * @hide
+ */
+interface IUwbAdapterStateCallbacks {
+ /**
+ * Called whenever the adapter state changes
+ *
+ * @param isEnabled true if the adapter is enabled, false otherwise
+ * @param reason the reason that the state has changed
+ */
+ void onAdapterStateChanged(boolean isEnabled, StateChangeReason reason);
+} \ No newline at end of file
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
new file mode 100644
index 000000000000..1fc3bfd818c3
--- /dev/null
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.os.PersistableBundle;
+import android.uwb.CloseReason;
+import android.uwb.RangingReport;
+import android.uwb.SessionHandle;
+import android.uwb.StartFailureReason;
+
+/**
+ * @hide
+ */
+interface IUwbRangingCallbacks {
+ /**
+ * Called when ranging has started
+ *
+ * May output parameters generated by the lower layers that must be sent to the
+ * remote device(s). The PersistableBundle must be constructed using the UWB
+ * support library.
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param rangingOutputParameters parameters generated by the lower layer that
+ * should be sent to the remote device.
+ */
+ void onRangingStarted(in SessionHandle sessionHandle,
+ in PersistableBundle parameters);
+
+ /**
+ * Called when a ranging session fails to start
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session failed to start
+ * @param parameters protocol specific parameters
+ */
+ void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason,
+ in PersistableBundle parameters);
+ /**
+ * Called when a ranging session is closed
+ *
+ * @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session was closed
+ * @param parameters protocol specific parameters
+ */
+ void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason,
+ in PersistableBundle parameters);
+
+ /**
+ * Provides a new RangingResult to the framework
+ *
+ * The reported timestamp for a ranging measurement must be calculated as the
+ * time which the ranging round that generated this measurement concluded.
+ *
+ * @param sessionHandle an identifier to associate the ranging results with a
+ * session that is active
+ * @param result the ranging report
+ */
+ void onRangingResult(in SessionHandle sessionHandle, in RangingReport result);
+}
diff --git a/core/java/android/uwb/MeasurementStatus.aidl b/core/java/android/uwb/MeasurementStatus.aidl
new file mode 100644
index 000000000000..5fa15549e84d
--- /dev/null
+++ b/core/java/android/uwb/MeasurementStatus.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum MeasurementStatus {
+ /**
+ * Ranging was successful
+ */
+ SUCCESS,
+
+ /**
+ * The remote device is out of range
+ */
+ FAILURE_OUT_OF_RANGE,
+
+ /**
+ * An unknown failure has occurred.
+ */
+ FAILURE_UNKNOWN,
+}
+
diff --git a/core/java/android/uwb/RangingReport.aidl b/core/java/android/uwb/RangingReport.aidl
new file mode 100644
index 000000000000..c32747ae58da
--- /dev/null
+++ b/core/java/android/uwb/RangingReport.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+parcelable RangingReport;
diff --git a/core/java/android/uwb/SessionHandle.aidl b/core/java/android/uwb/SessionHandle.aidl
new file mode 100644
index 000000000000..58a7dbb2ef9f
--- /dev/null
+++ b/core/java/android/uwb/SessionHandle.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+parcelable SessionHandle;
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
new file mode 100644
index 000000000000..928fcbdcf1c7
--- /dev/null
+++ b/core/java/android/uwb/SessionHandle.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public final class SessionHandle implements Parcelable {
+ private final int mId;
+
+ public SessionHandle(int id) {
+ mId = id;
+ }
+
+ protected SessionHandle(Parcel in) {
+ mId = in.readInt();
+ }
+
+ public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() {
+ @Override
+ public SessionHandle createFromParcel(Parcel in) {
+ return new SessionHandle(in);
+ }
+
+ @Override
+ public SessionHandle[] newArray(int size) {
+ return new SessionHandle[size];
+ }
+ };
+
+ public int getId() {
+ return mId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof SessionHandle) {
+ SessionHandle other = (SessionHandle) obj;
+ return mId == other.mId;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "SessionHandle [id=" + mId + "]";
+ }
+}
diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl
new file mode 100644
index 000000000000..4d9c962f529b
--- /dev/null
+++ b/core/java/android/uwb/StartFailureReason.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum StartFailureReason {
+ /**
+ * Unknown start failure reason
+ */
+ UNKNOWN,
+
+ /**
+ * The provided parameters were invalid and ranging could not start
+ */
+ BAD_PARAMETERS,
+
+ /**
+ * The maximum number of sessions has been reached. This error may be generated
+ * for an active session if a higher priority session begins.
+ */
+ MAX_SESSIONS_REACHED,
+
+ /**
+ * The system state has changed resulting in the session ending (e.g. the user
+ * disables UWB, or the user's locale changes and an active channel is no longer
+ * permitted to be used).
+ */
+ SYSTEM_POLICY,
+
+ /**
+ * The session could not start because of a protocol specific reason.
+ */
+ PROTOCOL_SPECIFIC,
+}
+
diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl
new file mode 100644
index 000000000000..46a6e2edfa22
--- /dev/null
+++ b/core/java/android/uwb/StateChangeReason.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+/**
+ * @hide
+ */
+@Backing(type="int")
+enum StateChangeReason {
+ /**
+ * The state changed for an unknown reason
+ */
+ UNKNOWN,
+
+ /**
+ * The adapter state changed because a session started.
+ */
+ SESSION_STARTED,
+
+
+ /**
+ * The adapter state changed because all sessions were closed.
+ */
+ ALL_SESSIONS_CLOSED,
+
+ /**
+ * The adapter state changed because of a device system change.
+ */
+ SYSTEM_POLICY,
+}
+
diff --git a/core/java/android/uwb/UwbAddress.aidl b/core/java/android/uwb/UwbAddress.aidl
new file mode 100644
index 000000000000..a202b1a1f51d
--- /dev/null
+++ b/core/java/android/uwb/UwbAddress.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+parcelable UwbAddress;
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
index 495528989a83..db9c53863c7f 100644
--- a/core/java/android/view/OnReceiveContentListener.java
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -48,7 +48,7 @@ import java.util.function.Predicate;
* public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
*
* &#64;Override
- * public Payload onReceiveContent(TextView view, Payload payload) {
+ * public Payload onReceiveContent(View view, Payload payload) {
* Map&lt;Boolean, Payload&gt; split = payload.partition(item -&gt; item.getUri() != null);
* if (split.get(true) != null) {
* ClipData clip = payload.getClip();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a88ad9f24c10..30ec2b050dbe 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9008,7 +9008,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Sets the listener to be {@link #onReceiveContent used} to handle insertion of
+ * Sets the listener to be {@link #performReceiveContent used} to handle insertion of
* content into this view.
*
* <p>Depending on the type of view, this listener may be invoked for different scenarios. For
@@ -9039,7 +9039,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* not be null or empty if a non-null listener is passed in.
* @param listener The listener to use. This can be null to reset to the default behavior.
*/
- @SuppressWarnings("rawtypes")
public void setOnReceiveContentListener(@Nullable String[] mimeTypes,
@Nullable OnReceiveContentListener listener) {
if (listener != null) {
@@ -9055,27 +9054,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Receives the given content. Invokes the listener configured via
- * {@link #setOnReceiveContentListener}; if no listener is set, the default implementation is a
- * no-op (returns the passed-in content without acting on it).
+ * Receives the given content. If no listener is set, invokes {@link #onReceiveContent}. If a
+ * listener is {@link #setOnReceiveContentListener set}, invokes the listener instead; if the
+ * listener returns a non-null result, invokes {@link #onReceiveContent} to handle it.
*
* @param payload The content to insert and related metadata.
*
* @return The portion of the passed-in content that was not accepted (may be all, some, or none
* of the passed-in content).
*/
- @SuppressWarnings({"rawtypes", "unchecked"})
- public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+ public @Nullable Payload performReceiveContent(@NonNull Payload payload) {
final OnReceiveContentListener listener = (mListenerInfo == null) ? null
: getListenerInfo().mOnReceiveContentListener;
if (listener != null) {
- return listener.onReceiveContent(this, payload);
+ final Payload remaining = listener.onReceiveContent(this, payload);
+ return (remaining == null) ? null : onReceiveContent(remaining);
}
+ return onReceiveContent(payload);
+ }
+
+ /**
+ * Implements the default behavior for receiving content for this type of view. The default
+ * view implementation is a no-op (returns the passed-in content without acting on it).
+ *
+ * <p>Widgets should override this method to define their default behavior for receiving
+ * content. Apps should {@link #setOnReceiveContentListener set a listener} to provide
+ * app-specific handling for receiving content.
+ *
+ * <p>See {@link #setOnReceiveContentListener} and {@link #performReceiveContent} for more info.
+ *
+ * @param payload The content to insert and related metadata.
+ *
+ * @return The portion of the passed-in content that was not handled (may be all, some, or none
+ * of the passed-in content).
+ */
+ public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
return payload;
}
/**
- * Returns the MIME types accepted by {@link #onReceiveContent} for this view, as
+ * Returns the MIME types accepted by {@link #performReceiveContent} for this view, as
* configured via {@link #setOnReceiveContentListener}. By default returns null.
*
* <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft
@@ -9092,7 +9110,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to
* lowercase.
*
- * @return The MIME types accepted by {@link #onReceiveContent} for this view (may
+ * @return The MIME types accepted by {@link #performReceiveContent} for this view (may
* include patterns such as "image/*").
*/
public @Nullable String[] getOnReceiveContentMimeTypes() {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 72f76d1a9dc9..436acef17c4d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -410,6 +410,13 @@ public interface WindowManager extends ViewManager {
int TRANSIT_FLAG_APP_CRASHED = 0x10;
/**
+ * Transition flag: A window in a new task is being opened behind an existing one in another
+ * activity's task.
+ * @hide
+ */
+ int TRANSIT_FLAG_OPEN_BEHIND = 0x20;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -417,7 +424,8 @@ public interface WindowManager extends ViewManager {
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
- TRANSIT_FLAG_APP_CRASHED
+ TRANSIT_FLAG_APP_CRASHED,
+ TRANSIT_FLAG_OPEN_BEHIND
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 299c41b02b23..8fdcac7b4f9d 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2372,7 +2372,7 @@ public final class AutofillManager {
return;
}
Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
- Payload result = view.onReceiveContent(payload);
+ Payload result = view.performReceiveContent(payload);
if (result != null) {
Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id
+ ", view=" + view + ", clip=" + clip);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a92d1f589e96..6a2a8d35a007 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -927,9 +927,9 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * Default implementation which invokes {@link View#onReceiveContent} on the target view if the
- * view {@link View#getOnReceiveContentMimeTypes allows} content insertion; otherwise returns
- * false without any side effects.
+ * Default implementation which invokes {@link View#performReceiveContent} on the target
+ * view if the view {@link View#getOnReceiveContentMimeTypes allows} content insertion;
+ * otherwise returns false without any side effects.
*/
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
ClipDescription description = inputContentInfo.getDescription();
@@ -954,6 +954,6 @@ public class BaseInputConnection implements InputConnection {
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
.build();
- return mTargetView.onReceiveContent(payload) == null;
+ return mTargetView.performReceiveContent(payload) == null;
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index da14f2cfbd5a..00ba326d2ba9 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2872,7 +2872,7 @@ public class Editor {
final OnReceiveContentListener.Payload payload =
new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
.build();
- mTextView.onReceiveContent(payload);
+ mTextView.performReceiveContent(payload);
if (dragDropIntoItself) {
deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fb13807495e6..98f808784803 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2152,7 +2152,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (isTextEditable()) {
ClipData clip = ClipData.newPlainText("", result);
Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
- onReceiveContent(payload);
+ performReceiveContent(payload);
if (mEditor != null) {
mEditor.refreshTextActionMode();
}
@@ -11858,7 +11858,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
- onReceiveContent(payload);
+ performReceiveContent(payload);
}
@Override
@@ -12927,7 +12927,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD)
.setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
.build();
- onReceiveContent(payload);
+ performReceiveContent(payload);
sLastCutCopyOrTextChangedTime = 0;
}
@@ -13728,17 +13728,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Receives the given content. Clients wishing to provide custom behavior should configure a
- * listener via {@link #setOnReceiveContentListener}.
+ * Default {@link TextView} implementation for receiving content. Apps wishing to provide
+ * custom behavior should configure a listener via {@link #setOnReceiveContentListener}.
*
- * <p>If a listener is set, invokes the listener. If the listener returns a non-null result,
- * executes the default platform handling for the portion of the content returned by the
- * listener.
- *
- * <p>If no listener is set, executes the default platform behavior. For non-editable TextViews
- * the default behavior is a no-op (returns the passed-in content without acting on it). For
- * editable TextViews the default behavior coerces all content to text and inserts into the
- * view.
+ * <p>For non-editable TextViews the default behavior is a no-op (returns the passed-in
+ * content without acting on it). For editable TextViews the default behavior coerces all
+ * content to text and inserts into the view.
*
* @param payload The content to insert and related metadata.
*
@@ -13747,11 +13742,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@Override
public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
- Payload remaining = super.onReceiveContent(payload);
- if (remaining != null && mEditor != null) {
- return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, remaining);
+ if (mEditor != null) {
+ return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload);
}
- return remaining;
+ return payload;
}
private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index c0d46f632500..571ac1bbeac6 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -37,34 +37,33 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
//TODO (163431584): need also consider other refresh rates.
private static final long JANK_THRESHOLD_NANOS = 1000000000 / 60;
private static final long UNKNOWN_TIMESTAMP = -1;
+ public static final int NANOS_IN_MILLISECOND = 1_000_000;
private final HardwareRendererObserver mObserver;
+ private final int mTraceThresholdMissedFrames;
+ private final int mTraceThresholdFrameTimeMillis;
private final ThreadedRendererWrapper mRendererWrapper;
private final FrameMetricsWrapper mMetricsWrapper;
private long mBeginTime = UNKNOWN_TIMESTAMP;
private long mEndTime = UNKNOWN_TIMESTAMP;
- private boolean mShouldTriggerTrace;
private boolean mSessionEnd;
+ private boolean mCancelled = false;
private int mTotalFramesCount = 0;
private int mMissedFramesCount = 0;
private long mMaxFrameTimeNanos = 0;
private Session mSession;
- /**
- * Constructor of FrameTracker.
- * @param session a trace session.
- * @param handler a handler for handling callbacks.
- * @param renderer a ThreadedRendererWrapper instance.
- * @param metrics a FrameMetricsWrapper instance.
- */
public FrameTracker(@NonNull Session session, @NonNull Handler handler,
- @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) {
+ @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics,
+ int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis) {
mSession = session;
mRendererWrapper = renderer;
mMetricsWrapper = metrics;
mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
+ mTraceThresholdMissedFrames = traceThresholdMissedFrames;
+ mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
}
/**
@@ -109,13 +108,15 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
}
Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
mRendererWrapper.removeObserver(mObserver);
- mBeginTime = UNKNOWN_TIMESTAMP;
- mEndTime = UNKNOWN_TIMESTAMP;
- mShouldTriggerTrace = false;
+ mCancelled = true;
}
@Override
public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+ if (mCancelled) {
+ return;
+ }
+
// Since this callback might come a little bit late after the end() call.
// We should keep tracking the begin / end timestamp.
// Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
@@ -136,13 +137,14 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
mTotalFramesCount);
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
- (int) (mMaxFrameTimeNanos / 1_000_000));
+ (int) (mMaxFrameTimeNanos / NANOS_IN_MILLISECOND));
// Trigger perfetto if necessary.
- if (mShouldTriggerTrace) {
- if (DEBUG) {
- Log.v(TAG, "Found janky frame, triggering perfetto.");
- }
+ boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
+ && mMissedFramesCount >= mTraceThresholdMissedFrames;
+ boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
+ && mMaxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
+ if (overMissedFramesThreshold || overFrameTimeThreshold) {
triggerPerfetto();
}
if (mSession.logToStatsd()) {
@@ -168,7 +170,6 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
if (isJankyFrame) {
mMissedFramesCount += 1;
- mShouldTriggerTrace = true;
}
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index fcaa963feda2..771a72c98a7a 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -36,7 +36,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Build;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
+import android.provider.DeviceConfig;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
@@ -47,6 +50,7 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
@@ -55,9 +59,21 @@ import java.util.concurrent.TimeUnit;
*/
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
- private static final boolean DEBUG = false;
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
+ private static final String SETTINGS_ENABLED_KEY = "enabled";
+ private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
+ "trace_threshold_missed_frames";
+ private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY =
+ "trace_threshold_frame_time_millis";
+ /** Default to being enabled on debug builds. */
+ private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
+ /** Default to collecting data for all CUJs. */
+ private static final int DEFAULT_SAMPLING_INTERVAL = 1;
+ /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */
+ private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
+ private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -106,12 +122,20 @@ public class InteractionJankMonitor {
private static volatile InteractionJankMonitor sInstance;
+ private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
+ this::updateProperties;
+
private ThreadedRendererWrapper mRenderer;
private FrameMetricsWrapper mMetrics;
private SparseArray<FrameTracker> mRunningTrackers;
private SparseArray<Runnable> mTimeoutActions;
private HandlerThread mWorker;
+
private boolean mInitialized;
+ private boolean mEnabled = DEFAULT_ENABLED;
+ private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
+ private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
+ private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
/** @hide */
@IntDef({
@@ -134,10 +158,12 @@ public class InteractionJankMonitor {
CUJ_NOTIFICATION_APP_START,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface CujType {}
+ public @interface CujType {
+ }
/**
* Get the singleton of InteractionJankMonitor.
+ *
* @return instance of InteractionJankMonitor
*/
public static InteractionJankMonitor getInstance() {
@@ -154,6 +180,7 @@ public class InteractionJankMonitor {
/**
* This constructor should be only public to tests.
+ *
* @param worker the worker thread for the callbacks
*/
@VisibleForTesting
@@ -165,11 +192,11 @@ public class InteractionJankMonitor {
/**
* Init InteractionJankMonitor for later instrumentation.
+ *
* @param view Any view in the view tree to get context and ThreadedRenderer.
* @return boolean true if the instance has been initialized successfully.
*/
public boolean init(@NonNull View view) {
- //TODO (163505250): This should be no-op if not in droid food rom.
if (!mInitialized) {
synchronized (this) {
if (!mInitialized) {
@@ -180,7 +207,20 @@ public class InteractionJankMonitor {
mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer());
mMetrics = new FrameMetricsWrapper();
mWorker.start();
+ mEnabled = DEFAULT_ENABLED;
+ mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
mInitialized = true;
+
+ // Post initialization to the background in case we're running on the main
+ // thread.
+ mWorker.getThreadHandler().post(
+ () -> mPropertiesChangedListener.onPropertiesChanged(
+ DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR)));
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR,
+ new HandlerExecutor(mWorker.getThreadHandler()),
+ mPropertiesChangedListener);
}
}
}
@@ -189,6 +229,7 @@ public class InteractionJankMonitor {
/**
* Create a {@link FrameTracker} instance.
+ *
* @param session the session associates with this tracker
* @return instance of the FrameTracker
*/
@@ -196,47 +237,50 @@ public class InteractionJankMonitor {
public FrameTracker createFrameTracker(Session session) {
synchronized (this) {
if (!mInitialized) return null;
- return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics);
+ return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics,
+ mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis);
}
}
/**
* Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ *
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
public boolean begin(@CujType int cujType) {
- //TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) {
- return begin(cujType, 0L /* timeout */);
+ return begin(cujType, DEFAULT_TIMEOUT_MS);
}
}
/**
* Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ *
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @param timeout the elapsed time in ms until firing the timeout action.
* @return boolean true if the tracker is started successfully, false otherwise.
*/
public boolean begin(@CujType int cujType, long timeout) {
- //TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) {
if (!mInitialized) {
Log.d(TAG, "Not initialized!", new Throwable());
return false;
}
- Session session = new Session(cujType);
- FrameTracker tracker = getTracker(session);
+ boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ if (!mEnabled || !shouldSample) {
+ return false;
+ }
+ FrameTracker tracker = getTracker(cujType);
// Skip subsequent calls if we already have an ongoing tracing.
if (tracker != null) return false;
// begin a new trace session.
- tracker = createFrameTracker(session);
+ tracker = createFrameTracker(new Session(cujType));
mRunningTrackers.put(cujType, tracker);
tracker.begin();
// Cancel the trace if we don't get an end() call in specified duration.
- timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS;
Runnable timeoutAction = () -> cancel(cujType);
mTimeoutActions.put(cujType, timeoutAction);
mWorker.getThreadHandler().postDelayed(timeoutAction, timeout);
@@ -246,6 +290,7 @@ public class InteractionJankMonitor {
/**
* End a trace session, must invoke {@link #init(View)} before invoking this method.
+ *
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
@@ -263,18 +308,18 @@ public class InteractionJankMonitor {
mTimeoutActions.remove(cujType);
}
- Session session = new Session(cujType);
- FrameTracker tracker = getTracker(session);
+ FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
tracker.end();
- mRunningTrackers.remove(session.getCuj());
+ mRunningTrackers.remove(cujType);
return true;
}
}
/**
* Cancel the trace session, must invoke {@link #init(View)} before invoking this method.
+ *
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
@@ -291,48 +336,38 @@ public class InteractionJankMonitor {
mTimeoutActions.remove(cujType);
}
- Session session = new Session(cujType);
- FrameTracker tracker = getTracker(session);
+ FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
tracker.cancel();
- mRunningTrackers.remove(session.getCuj());
+ mRunningTrackers.remove(cujType);
return true;
}
}
- private void destroy() {
+ private FrameTracker getTracker(@CujType int cuj) {
synchronized (this) {
- int trackers = mRunningTrackers.size();
- for (int i = 0; i < trackers; i++) {
- mRunningTrackers.valueAt(i).cancel();
- }
- mRunningTrackers = null;
- mTimeoutActions.clear();
- mTimeoutActions = null;
- mWorker.quit();
- mWorker = null;
+ if (!mInitialized) return null;
+ return mRunningTrackers.get(cuj);
}
}
- /**
- * Abandon current instance.
- */
- @VisibleForTesting
- public static void abandon() {
- if (sInstance == null) return;
- synchronized (InteractionJankMonitor.class) {
- if (sInstance == null) return;
- sInstance.destroy();
- sInstance = null;
+ private void updateProperties(DeviceConfig.Properties properties) {
+ synchronized (this) {
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
+ DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+ mTraceThresholdFrameTimeMillis = properties.getInt(
+ SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
+ DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
}
}
- private FrameTracker getTracker(Session session) {
- synchronized (this) {
- if (!mInitialized) return null;
- return mRunningTrackers.get(session.getCuj());
- }
+ @VisibleForTesting
+ public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() {
+ return mPropertiesChangedListener;
}
/**
@@ -402,12 +437,14 @@ public class InteractionJankMonitor {
* A class to represent a session.
*/
public static class Session {
- private @CujType int mCujType;
+ @CujType
+ private int mCujType;
public Session(@CujType int cujType) {
mCujType = cujType;
}
+ @CujType
public int getCuj() {
return mCujType;
}
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 41be5c493d95..2237efc9e2b6 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -57,6 +57,7 @@ public class SystemNotificationChannels {
public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
public static String DO_NOT_DISTURB = "DO_NOT_DISTURB";
+ public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -191,6 +192,13 @@ public class SystemNotificationChannels {
NotificationManager.IMPORTANCE_LOW);
channelsList.add(dndChanges);
+ final NotificationChannel newFeaturePrompt = new NotificationChannel(
+ ACCESSIBILITY_MAGNIFICATION,
+ context.getString(R.string.notification_channel_accessibility_magnification),
+ NotificationManager.IMPORTANCE_HIGH);
+ newFeaturePrompt.setBlockable(true);
+ channelsList.add(newFeaturePrompt);
+
nm.createNotificationChannels(channelsList);
}
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c110b263e781..c85b5d7aa7a6 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -18,9 +18,10 @@
package com.android.internal.os;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.BasicShellCommandHandler;
import android.os.Build;
+import com.android.modules.utils.BasicShellCommandHandler;
+
import java.io.PrintStream;
public abstract class BaseCommand {
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 70b1ad49d8b8..aa42979ed53c 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -56,6 +56,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000;
public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false;
public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true;
+ public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false;
public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500;
private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
@@ -95,6 +96,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
private boolean mAddDebugEntries = false;
private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID;
private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE;
+ private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS;
private CachedDeviceState.Readonly mDeviceState;
private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
@@ -185,8 +187,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public CallSession callStarted(Binder binder, int code, int workSourceUid) {
noteNativeThreadId();
- if (!mRecordingAllTransactionsForUid
- && (mDeviceState == null || mDeviceState.isCharging())) {
+ if (!canCollect()) {
return null;
}
@@ -255,8 +256,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
synchronized (mLock) {
// This was already checked in #callStart but check again while synchronized.
- if (!mRecordingAllTransactionsForUid
- && (mDeviceState == null || mDeviceState.isCharging())) {
+ if (!canCollect()) {
return;
}
@@ -372,6 +372,22 @@ public class BinderCallsStats implements BinderInternal.Observer {
mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids());
}
+ private boolean canCollect() {
+ if (mRecordingAllTransactionsForUid) {
+ return true;
+ }
+ if (mIgnoreBatteryStatus) {
+ return true;
+ }
+ if (mDeviceState == null) {
+ return false;
+ }
+ if (mDeviceState.isCharging()) {
+ return false;
+ }
+ return true;
+ }
+
/**
* This method is expensive to call.
*/
@@ -672,6 +688,18 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
/**
+ * Whether to ignore battery status when collecting stats
+ */
+ public void setIgnoreBatteryStatus(boolean ignored) {
+ synchronized (mLock) {
+ if (ignored != mIgnoreBatteryStatus) {
+ mIgnoreBatteryStatus = ignored;
+ reset();
+ }
+ }
+ }
+
+ /**
* Marks the specified work source UID for total binder call tracking: detailed information
* will be recorded for all calls from this source ID.
*
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 932ff572219f..2805dccffe50 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -40,6 +40,7 @@ public class LooperStats implements Looper.Observer {
public static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
private static final int SESSION_POOL_SIZE = 50;
private static final boolean DISABLED_SCREEN_STATE_TRACKING_VALUE = false;
+ public static final boolean DEFAULT_IGNORE_BATTERY_STATUS = false;
@GuardedBy("mLock")
private final SparseArray<Entry> mEntries = new SparseArray<>(512);
@@ -56,6 +57,7 @@ public class LooperStats implements Looper.Observer {
private long mStartElapsedTime = SystemClock.elapsedRealtime();
private boolean mAddDebugEntries = false;
private boolean mTrackScreenInteractive = false;
+ private boolean mIgnoreBatteryStatus = DEFAULT_IGNORE_BATTERY_STATUS;
public LooperStats(int samplingInterval, int entriesSizeCap) {
this.mSamplingInterval = samplingInterval;
@@ -139,8 +141,16 @@ public class LooperStats implements Looper.Observer {
}
private boolean deviceStateAllowsCollection() {
- // Do not collect data if on charger or the state is not set.
- return mDeviceState != null && !mDeviceState.isCharging();
+ if (mIgnoreBatteryStatus) {
+ return true;
+ }
+ if (mDeviceState == null) {
+ return false;
+ }
+ if (mDeviceState.isCharging()) {
+ return false;
+ }
+ return true;
}
/** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
@@ -225,6 +235,10 @@ public class LooperStats implements Looper.Observer {
mTrackScreenInteractive = enabled;
}
+ public void setIgnoreBatteryStatus(boolean ignore) {
+ mIgnoreBatteryStatus = ignore;
+ }
+
@Nullable
private Entry findEntry(Message msg, boolean allowCreateNew) {
final boolean isInteractive = mTrackScreenInteractive
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index a87e8aaa0e64..8012540ed314 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -15,15 +15,11 @@
package com.android.internal.util;
import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.util.EventLog;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.SparseLongArray;
@@ -135,8 +131,16 @@ public class LatencyTracker {
mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
// Post initialization to the background in case we're running on the main thread.
- BackgroundThread.getHandler().post(this::registerSettingsObserver);
- BackgroundThread.getHandler().post(this::readSettings);
+ BackgroundThread.getHandler().post(() -> this.updateProperties(
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_LATENCY_TRACKER)));
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
+ BackgroundThread.getExecutor(), this::updateProperties);
+ }
+
+ private void updateProperties(DeviceConfig.Properties properties) {
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
}
/**
@@ -171,28 +175,6 @@ public class LatencyTracker {
}
}
- private void registerSettingsObserver() {
- Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
- mContext.getContentResolver().registerContentObserver(
- settingsUri, false, new SettingsObserver(this), UserHandle.myUserId());
- }
-
- private void readSettings() {
- KeyValueListParser parser = new KeyValueListParser(',');
- String settingsValue = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.LATENCY_TRACKER);
-
- try {
- parser.setString(settingsValue);
- mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Incorrect settings format", e);
- mEnabled = false;
- }
- }
-
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
@@ -236,8 +218,8 @@ public class LatencyTracker {
/**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
*/
public void logAction(int action, int duration) {
boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
@@ -260,18 +242,4 @@ public class LatencyTracker {
FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
}
}
-
- private static class SettingsObserver extends ContentObserver {
- private final LatencyTracker mThisTracker;
-
- SettingsObserver(LatencyTracker thisTracker) {
- super(BackgroundThread.getHandler());
- mThisTracker = thisTracker;
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- mThisTracker.readSettings();
- }
- }
}
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 48f33a6a3d77..781895eeeaba 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -20,6 +20,8 @@
#include "android_os_HwBinder.h"
+#include "android_util_Binder.h" // for binder_report_exception
+
#include "android_os_HwParcel.h"
#include "android_os_HwRemoteBinder.h"
@@ -183,15 +185,7 @@ status_t JHwBinder::onTransact(
env->ExceptionDescribe();
env->ExceptionClear();
- // It is illegal to call IsInstanceOf if there is a pending exception.
- // Attempting to do so results in a JniAbort which crashes the entire process.
- if (env->IsInstanceOf(excep, gErrorClass)) {
- /* It's an error */
- LOG(ERROR) << "Forcefully exiting";
- _exit(1);
- } else {
- LOG(ERROR) << "Uncaught exception!";
- }
+ binder_report_exception(env, excep, "Uncaught error or exception in hwbinder!");
env->DeleteLocalRef(excep);
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a3cb4c076593..7c9b8625c8ee 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -304,8 +304,9 @@ static void report_java_lang_error(JNIEnv* env, jthrowable error, const char* ms
report_java_lang_error_fatal_error(env, error, msg);
}
-static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
-{
+namespace android {
+
+void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg) {
env->ExceptionClear();
ScopedLocalRef<jstring> tagstr(env, env->NewStringUTF(LOG_TAG));
@@ -333,6 +334,8 @@ static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
}
}
+} // namespace android
+
class JavaBBinderHolder;
class JavaBBinder : public BBinder
@@ -407,9 +410,9 @@ protected:
if (env->ExceptionCheck()) {
ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
- report_exception(env, excep.get(),
- "*** Uncaught remote exception! "
- "(Exceptions are not yet supported across processes.)");
+ binder_report_exception(env, excep.get(),
+ "*** Uncaught remote exception! "
+ "(Exceptions are not yet supported across processes.)");
res = JNI_FALSE;
}
@@ -423,8 +426,8 @@ protected:
if (env->ExceptionCheck()) {
ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
- report_exception(env, excep.get(),
- "*** Uncaught exception in onBinderStrictModePolicyChange");
+ binder_report_exception(env, excep.get(),
+ "*** Uncaught exception in onBinderStrictModePolicyChange");
}
// Need to always call through the native implementation of
@@ -569,8 +572,8 @@ public:
jBinderProxy.get());
if (env->ExceptionCheck()) {
jthrowable excep = env->ExceptionOccurred();
- report_exception(env, excep,
- "*** Uncaught exception returned from death notification!");
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from death notification!");
}
// Serialize with our containing DeathRecipientList so that we can't
@@ -1163,8 +1166,8 @@ static void android_os_BinderInternal_proxyLimitcallback(int uid)
if (env->ExceptionCheck()) {
ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
- report_exception(env, excep.get(),
- "*** Uncaught exception in binderProxyLimitCallbackFromNative");
+ binder_report_exception(env, excep.get(),
+ "*** Uncaught exception in binderProxyLimitCallbackFromNative");
}
}
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index c109d6c265c3..9098d46ee29c 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -35,6 +35,8 @@ extern void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy);
extern void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
bool canThrowRemoteException = false, int parcelSize = 0);
+// does not take ownership of the exception, aborts if this is an error
+void binder_report_exception(JNIEnv* env, jthrowable excep, const char* msg);
}
#endif
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6ffafc16b89c..3156f71fa113 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -78,7 +78,6 @@
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
#include <bionic/mte.h>
-#include <bionic/mte_kernel.h>
#include <cutils/fs.h>
#include <cutils/memory.h>
#include <cutils/multiuser.h>
@@ -1650,28 +1649,6 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
}
}
-#ifdef ANDROID_EXPERIMENTAL_MTE
-static void SetTagCheckingLevel(int level) {
-#ifdef __aarch64__
- if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
- return;
- }
-
- int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
- if (tagged_addr_ctrl < 0) {
- ALOGE("prctl(PR_GET_TAGGED_ADDR_CTRL) failed: %s", strerror(errno));
- return;
- }
-
- tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
- if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
- ALOGE("prctl(PR_SET_TAGGED_ADDR_CTRL, %d) failed: %s", tagged_addr_ctrl,
- strerror(errno));
- }
-#endif
-}
-#endif
-
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
@@ -1807,22 +1784,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
break;
case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
-#ifdef ANDROID_EXPERIMENTAL_MTE
- SetTagCheckingLevel(PR_MTE_TCF_ASYNC);
-#endif
heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
break;
case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
-#ifdef ANDROID_EXPERIMENTAL_MTE
- SetTagCheckingLevel(PR_MTE_TCF_SYNC);
-#endif
heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
break;
default:
-#ifdef ANDROID_EXPERIMENTAL_MTE
- SetTagCheckingLevel(PR_MTE_TCF_NONE);
-#endif
heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+ break;
}
android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
diff --git a/core/proto/android/stats/tv/tif_enums.proto b/core/proto/android/stats/tv/tif_enums.proto
new file mode 100644
index 000000000000..a9028e529186
--- /dev/null
+++ b/core/proto/android/stats/tv/tif_enums.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.tv;
+option java_multiple_files = true;
+
+// Enums for TV Input Framework
+option java_outer_classname = "TifStatsEnums";
+
+// Tune State of a TV Input Service Framework
+enum TifTuneState {
+ TIF_TUNE_STATE_UNKNOWN = 0;
+ CREATED = 1;
+ SURFACE_ATTACHED = 2;
+ SURFACE_DETACHED = 3;
+ RELEASED = 4;
+ TUNE_STARTED = 5;
+ VIDEO_AVAILABLE = 6;
+
+ // Keep in sync with TvInputManager
+ // Use the TvInputManager value + 100
+ VIDEO_UNAVAILABLE_REASON_UNKNOWN = 100;
+ VIDEO_UNAVAILABLE_REASON_TUNING = 101;
+ VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 102;
+ VIDEO_UNAVAILABLE_REASON_BUFFERING = 103;
+ VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 104;
+ VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 105;
+ VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 106;
+ VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION=107;
+ VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED=108;
+ VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE=109;
+ VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED=110;
+ VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION=111;
+ VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING=112;
+ VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD=113;
+ VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE=114;
+ VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID=115;
+ VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT=116;
+ VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING=117;
+ VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN=118;
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8eb0853fda24..be6b6b15fe4d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -717,6 +717,10 @@
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6665375982962336520] -->
<string name="notification_channel_foreground_service">Apps consuming battery</string>
+ <!-- Text shown when viewing channel settings for notifications related to accessibility
+ magnification. [CHAR_LIMIT=NONE]-->
+ <string name="notification_channel_accessibility_magnification">Magnification</string>
+
<!-- Label for foreground service notification when one app is running.
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
<string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is
@@ -5767,4 +5771,16 @@ ul.</string>
<string name="config_pdp_reject_service_not_subscribed"></string>
<!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] -->
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string>
+
+ <!-- Window magnification prompt related string. -->
+
+ <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
+ <string name="window_magnification_prompt_title">New: Window Magnifier</string>
+ <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] -->
+ <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string>
+ <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] -->
+ <string name="turn_on_magnification_settings_action">Turn on in Settings</string>
+ <!-- Notification action to dismiss. [CHAR LIMIT=50] -->
+ <string name="dismiss_action">Dismiss</string>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 23733af4455e..752bb5b37a30 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3458,6 +3458,7 @@
<java-symbol type="string" name="notification_channel_heavy_weight_app" />
<java-symbol type="string" name="notification_channel_system_changes" />
<java-symbol type="string" name="notification_channel_do_not_disturb" />
+ <java-symbol type="string" name="notification_channel_accessibility_magnification" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
@@ -4096,4 +4097,10 @@
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
+
+ <!-- Window magnification prompt -->
+ <java-symbol type="string" name="window_magnification_prompt_title" />
+ <java-symbol type="string" name="window_magnification_prompt_content" />
+ <java-symbol type="string" name="turn_on_magnification_settings_action" />
+ <java-symbol type="string" name="dismiss_action" />
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index e17800f7b6fd..2402420e1bc1 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -77,7 +77,7 @@ public class FrameTrackerTest {
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
mTracker = Mockito.spy(
- new FrameTracker(session, handler, mRenderer, mWrapper));
+ new FrameTracker(session, handler, mRenderer, mWrapper, 1, -1));
doNothing().when(mTracker).triggerPerfetto();
}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index a9cfd286688b..474cb1d7ed96 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -32,6 +33,7 @@ import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.provider.DeviceConfig;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -50,6 +52,7 @@ import org.mockito.ArgumentCaptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
@@ -71,8 +74,6 @@ public class InteractionJankMonitorTest {
mView = mActivity.getWindow().getDecorView();
assertThat(mView.isAttachedToWindow()).isTrue();
- InteractionJankMonitor.abandon();
-
Handler handler = spy(new Handler(mActivity.getMainLooper()));
doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
mWorker = spy(new HandlerThread("Interaction-jank-monitor-test"));
@@ -93,7 +94,7 @@ public class InteractionJankMonitorTest {
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new FrameMetricsWrapper()));
+ new FrameMetricsWrapper(), 1, -1));
doReturn(tracker).when(monitor).createFrameTracker(any());
// Simulate a trace session and see if begin / end are invoked.
@@ -104,6 +105,21 @@ public class InteractionJankMonitorTest {
}
@Test
+ public void testDisabledThroughDeviceConfig() {
+ InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+ monitor.init(mView);
+
+ HashMap<String, String> propertiesValues = new HashMap<>();
+ propertiesValues.put("enabled", "false");
+ DeviceConfig.Properties properties = new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues);
+ monitor.getPropertiesChangedListener().onPropertiesChanged(properties);
+
+ assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ }
+
+ @Test
public void testCheckInitState() {
InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
@@ -134,12 +150,13 @@ public class InteractionJankMonitorTest {
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new FrameMetricsWrapper()));
+ new FrameMetricsWrapper(), 1, -1));
doReturn(tracker).when(monitor).createFrameTracker(any());
assertThat(monitor.begin(session.getCuj())).isTrue();
verify(tracker).begin();
- verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong());
+ verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(),
+ anyLong());
Runnable runnable = captor.getValue().getCallback();
assertThat(runnable).isNotNull();
mWorker.getThreadHandler().removeCallbacks(runnable);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 3117935fb3ed..561c549676da 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -46,7 +46,6 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
@@ -428,6 +427,19 @@ public class BinderCallsStatsTest {
}
@Test
+ public void testIgnoreBatteryStatusFlag() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ mDeviceState.setCharging(true);
+ bcs.setIgnoreBatteryStatus(true);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ assertEquals(1, bcs.getExportedCallStats().size());
+ }
+
+ @Test
public void testScreenOff() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 7917a06cb9b7..fdfc7ac5587c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -322,6 +322,23 @@ public final class LooperStatsTest {
}
@Test
+ public void testDataCollectedIfIgnoreBatteryStatusFlagSet() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ mDeviceState.setCharging(true);
+ looperStats.setIgnoreBatteryStatus(true);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(1);
+
+ }
+
+ @Test
public void testScreenStateCollected() {
TestableLooperStats looperStats = new TestableLooperStats(1, 100);
diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
new file mode 100644
index 000000000000..8b42ff7f62a7
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link SessionHandle}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SessionHandleTest {
+
+ @Test
+ public void testBasic() {
+ int handleId = 12;
+ SessionHandle handle = new SessionHandle(handleId);
+ assertEquals(handle.getId(), handleId);
+ }
+
+ @Test
+ public void testParcel() {
+ Parcel parcel = Parcel.obtain();
+ SessionHandle handle = new SessionHandle(10);
+ handle.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel);
+ assertEquals(handle, fromParcel);
+ }
+}
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index fe1182ecad4f..e473c55ce010 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -56,5 +56,7 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
+ <permission name="android.permission.READ_DREAM_STATE"/>
+ <permission name="android.permission.READ_DREAM_SUPPRESSION"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index eacb6295f96f..52da707565d7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -241,12 +241,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-1847087163": {
- "message": "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps",
- "level": "DEBUG",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1844540996": {
"message": " Initial targets: %s",
"level": "VERBOSE",
@@ -271,6 +265,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "-1800899273": {
+ "message": "applyAnimation: anim=%s transit=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
"-1792633344": {
"message": "Register task organizer=%s uid=%d",
"level": "VERBOSE",
@@ -913,12 +913,6 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-1044506655": {
- "message": "New transit away from wallpaper: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransitionController.java"
- },
"-1042574499": {
"message": "Attempted to add Accessibility overlay window with unknown token %s. Aborting.",
"level": "WARN",
@@ -985,12 +979,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-928291778": {
- "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
- "at": "com\/android\/server\/wm\/AppTransition.java"
- },
"-927199900": {
"message": "Updating global configuration to: %s",
"level": "INFO",
@@ -1117,12 +1105,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-793346159": {
- "message": "New transit into wallpaper: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransitionController.java"
- },
"-784959154": {
"message": "Attempted to add private presentation window to a non-private display. Aborting.",
"level": "WARN",
@@ -1195,6 +1177,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-701167286": {
+ "message": "applyAnimation: transit=%s, enter=%b, wc=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"-694710814": {
"message": "Pausing rotation during drag",
"level": "DEBUG",
@@ -1597,6 +1585,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-240296576": {
+ "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"-235225312": {
"message": "Skipping config check for initializing activity: %s",
"level": "VERBOSE",
@@ -1657,12 +1651,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-129722369": {
- "message": "New transit: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransitionController.java"
- },
"-118786523": {
"message": "Resume failed; resetting state to %s: %s",
"level": "VERBOSE",
@@ -3289,6 +3277,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "1810019902": {
+ "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"1822843721": {
"message": "Aborted starting %s: startingData=%s",
"level": "VERBOSE",
@@ -3433,12 +3427,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "2016061474": {
- "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/AppTransition.java"
- },
"2018454757": {
"message": "WS.removeImmediately: %s Already removed...",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING
new file mode 100644
index 000000000000..1018702e01c5
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/TEST_MAPPING
@@ -0,0 +1,24 @@
+{
+ "presubmit": [
+ {
+
+ "name": "CtsGraphicsTestCases",
+ "file_patterns": ["(/|^)Icon\\.java"],
+ "options" : [
+ {
+ "include-filter": "android.graphics.drawable.cts.IconTest"
+ }
+ ]
+ },
+ {
+
+ "name": "FrameworksCoreTests",
+ "file_patterns": ["(/|^)Icon\\.java"],
+ "options" : [
+ {
+ "include-filter": "android.graphics.drawable.IconTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 9af15a5f4a16..49a48871fd30 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -17,11 +17,11 @@
package android.security;
import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreOperation;
-import android.system.keystore2.KeyParameter;
import android.system.keystore2.ResponseCode;
import android.util.Log;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 9d3b62278ba0..7c3de8bee475 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -18,6 +18,7 @@ package android.security;
import android.annotation.NonNull;
import android.app.compat.CompatChanges;
+import android.hardware.keymint.KeyParameter;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.security.keystore.BackendBusyException;
@@ -27,7 +28,6 @@ import android.system.keystore2.CreateOperationResponse;
import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
import android.system.keystore2.ResponseCode;
import android.util.Log;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 3ac9d68d5a9f..5867ef6eaea5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -84,7 +84,7 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyStore
put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
- put("alg.alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore");
+ put("Alg.Alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore");
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 70713a47ad6d..69c7a2589d6f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -17,10 +17,10 @@
package android.security.keystore2;
import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index dd094b7a5fd0..2b5f6c31607b 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -18,13 +18,13 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
import android.security.keystore2.KeyStoreCryptoOperationChunkedStreamer.Stream;
-import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index b785ee5c6966..18d26922f1ae 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -19,11 +19,11 @@ package android.security.keystore2;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyStoreCryptoOperation;
-import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 9f7f2383a416..2250c89aac41 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -17,12 +17,12 @@
package android.security.keystore2;
import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 3dde2e592259..eea45c287622 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -16,12 +16,12 @@
package android.security.keystore2;
+import android.hardware.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyStoreCryptoOperation;
import android.security.keystore.KeymasterUtils;
-import android.system.keystore2.KeyParameter;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index ccd0a4bf92ff..479fd8a6a73a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -16,6 +16,8 @@
package android.security.keystore2;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterArguments;
@@ -29,8 +31,6 @@ import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
-import android.system.keystore2.SecurityLevel;
import android.util.Log;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index a747a0e727d8..61725e3e8c24 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,6 +18,8 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
@@ -35,9 +37,7 @@ import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
import android.system.keystore2.ResponseCode;
-import android.system.keystore2.SecurityLevel;
import android.util.Log;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index a6ea9723db24..2686ddc20c1d 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,11 +18,11 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeymasterUtils;
import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
index 5f1b9c0586a1..444dad4cffbe 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java
@@ -17,9 +17,9 @@
package android.security.keystore2;
import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
import java.security.InvalidKeyException;
import java.security.SignatureSpi;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
index 55414b70d403..a168f8feb3db 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java
@@ -18,12 +18,12 @@ package android.security.keystore2;
import android.annotation.CallSuper;
import android.annotation.NonNull;
+import android.hardware.keymint.KeyParameter;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyStoreCryptoOperation;
-import android.system.keystore2.KeyParameter;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 4c26864cb02b..9790a4ae5b65 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -18,6 +18,9 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.keymint.HardwareAuthenticatorType;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -36,9 +39,7 @@ import android.system.keystore2.IKeystoreSecurityLevel;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
import android.system.keystore2.KeyMetadata;
-import android.system.keystore2.KeyParameter;
import android.system.keystore2.ResponseCode;
-import android.system.keystore2.SecurityLevel;
import android.util.Log;
import java.io.ByteArrayInputStream;
@@ -871,16 +872,13 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
List<AuthenticatorSpec> authenticatorSpecs = new ArrayList<>();
AuthenticatorSpec authenticatorSpec = new AuthenticatorSpec();
- // TODO Replace with HardwareAuthenticatorType.PASSWORD when KeyMint AIDL spec has landed.
- authenticatorSpec.authenticatorType = 1; // HardwareAuthenticatorType.PASSWORD
+ authenticatorSpec.authenticatorType = HardwareAuthenticatorType.PASSWORD;
authenticatorSpec.authenticatorId = GateKeeper.getSecureUserId();
authenticatorSpecs.add(authenticatorSpec);
for (long sid : biometricSids) {
AuthenticatorSpec authSpec = new AuthenticatorSpec();
- // TODO Replace with HardwareAuthenticatorType.FINGERPRINT when KeyMint AIDL spec has
- // landed.
- authSpec.authenticatorType = 2; // HardwareAuthenticatorType.FINGERPRINT
+ authSpec.authenticatorType = HardwareAuthenticatorType.FINGERPRINT;
authSpec.authenticatorId = sid;
authenticatorSpecs.add(authSpec);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index 3d5a8f63e7f9..a2d4528b99fd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -18,10 +18,10 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyProperties;
-import android.system.keystore2.KeyParameter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index ee67ed3f76d8..8fa532b6e188 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -18,13 +18,13 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.keymint.KeyParameter;
+import android.hardware.keymint.SecurityLevel;
import android.security.GateKeeper;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserAuthArgs;
import android.system.keystore2.Authorization;
-import android.system.keystore2.KeyParameter;
-import android.system.keystore2.SecurityLevel;
import java.security.ProviderException;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index beac59b699fa..aa7355b61eda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -89,6 +89,7 @@ public class BubbleController implements Bubbles {
// TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast
public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged";
+ public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated";
public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened";
public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible";
public static final String EXTRA_TASKBAR_POSITION = "taskbarPosition";
@@ -350,12 +351,15 @@ public class BubbleController implements Bubbles {
+ " itemPosition: " + itemPosition[0] + "," + itemPosition[1]
+ " iconSize: " + iconSize);
PointF point = new PointF(itemPosition[0], itemPosition[1]);
- mBubblePositioner.setPinnedLocation(point);
+ mBubblePositioner.setPinnedLocation(isVisible ? point : null);
mBubblePositioner.updateForTaskbar(iconSize, taskbarPosition, isVisible, taskbarSize);
if (mStackView != null) {
- if (isVisible) {
- mStackView.updateStackPosition();
+ if (isVisible && b.getBoolean(EXTRA_TASKBAR_CREATED, false /* default */)) {
+ // If taskbar was created, add and remove the window so that bubbles display on top
+ removeFromWindowManagerMaybe();
+ addToWindowManagerMaybe();
}
+ mStackView.updateStackPosition();
mBubbleIconFactory = new BubbleIconFactory(mContext);
mStackView.onDisplaySizeChanged();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index df6683ebb80b..1bb5eda25058 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.Size;
@@ -42,6 +43,9 @@ public class PipBoundsAlgorithm {
private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
+ private float mDefaultSizePercent;
+ private float mMinAspectRatioForMinSize;
+ private float mMaxAspectRatioForMinSize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
@@ -51,7 +55,7 @@ public class PipBoundsAlgorithm {
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) {
mPipBoundsState = pipBoundsState;
- mSnapAlgorithm = new PipSnapAlgorithm(context);
+ mSnapAlgorithm = new PipSnapAlgorithm();
reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
@@ -83,6 +87,11 @@ public class PipBoundsAlgorithm {
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
mMaxAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+ mDefaultSizePercent = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
+ mMaxAspectRatioForMinSize = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
}
/**
@@ -174,7 +183,7 @@ public class PipBoundsAlgorithm {
final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
: defaultMinEdgeSize;
// Use the existing size but adjusted to the aspect ratio and min edge size.
- size = mSnapAlgorithm.getSizeForAspectRatio(
+ size = getSizeForAspectRatio(
new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
} else {
if (overrideMinSize != null) {
@@ -184,7 +193,7 @@ public class PipBoundsAlgorithm {
} else {
// Calculate the default size using the display size and default min edge size.
final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
- size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
+ size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
displayInfo.logicalWidth, displayInfo.logicalHeight);
}
}
@@ -229,7 +238,7 @@ public class PipBoundsAlgorithm {
defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
} else {
// Calculate the default size using the display size and default min edge size.
- defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+ defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
}
Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(),
@@ -270,13 +279,28 @@ public class PipBoundsAlgorithm {
getInsetBounds(movementBounds);
// Apply the movement bounds adjustments based on the current state.
- mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+ getMovementBounds(stackBounds, movementBounds, movementBounds,
(adjustForIme && mPipBoundsState.isImeShowing())
? mPipBoundsState.getImeHeight() : 0);
+
return movementBounds;
}
/**
+ * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds.
+ */
+ public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
+ int bottomOffset) {
+ // Adjust the right/bottom to ensure the stack bounds never goes offscreen
+ movementBoundsOut.set(insetBounds);
+ movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right
+ - stackBounds.width());
+ movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom
+ - stackBounds.height());
+ movementBoundsOut.bottom -= bottomOffset;
+ }
+
+ /**
* @return the default snap fraction to apply instead of the default gravity when calculating
* the default stack bounds when first entering PiP.
*/
@@ -304,6 +328,62 @@ public class PipBoundsAlgorithm {
}
/**
+ * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge
+ * is at least minEdgeSize.
+ */
+ public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
+ int displayHeight) {
+ final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
+ final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
+ // Beyond these points, we can just use the min size as the shorter edge
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ } else {
+ // Within these points, we ensure that the bounds fit within the radius of the limits
+ // at the points
+ final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+ final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+ height = (int) Math.round(Math.sqrt((radius * radius)
+ / (aspectRatio * aspectRatio + 1)));
+ width = Math.round(height * aspectRatio);
+ }
+ return new Size(width, height);
+ }
+
+ /**
+ * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
+ * minimum edge is at least minEdgeSize.
+ */
+ public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
+ final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+ final int minSize = (int) Math.max(minEdgeSize, smallestSize);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size.
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ return new Size(width, height);
+ }
+
+ /**
* Dumps internal states.
*/
public void dump(PrintWriter pw, String prefix) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
index 71060752df09..0528e4d88374 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
@@ -20,11 +20,9 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PointF;
import android.graphics.Rect;
-import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* Calculates the snap targets and the snap position for the PIP given a position and a velocity.
@@ -32,19 +30,6 @@ import android.util.Size;
*/
public class PipSnapAlgorithm {
- private final float mDefaultSizePercent;
- private final float mMinAspectRatioForMinSize;
- private final float mMaxAspectRatioForMinSize;
-
- public PipSnapAlgorithm(Context context) {
- Resources res = context.getResources();
- mDefaultSizePercent = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
- mMaxAspectRatioForMinSize = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
- mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
- }
-
/**
* Returns a fraction that describes where the PiP bounds is.
* See {@link #getSnapFraction(Rect, Rect, int)}.
@@ -136,81 +121,11 @@ public class PipSnapAlgorithm {
}
/**
- * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
- * {@param stackBounds}.
- */
- public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
- int bottomOffset) {
- // Adjust the right/bottom to ensure the stack bounds never goes offscreen
- movementBoundsOut.set(insetBounds);
- movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right -
- stackBounds.width());
- movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom -
- stackBounds.height());
- movementBoundsOut.bottom -= bottomOffset;
- }
-
- /**
- * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge
- * is at least {@param minEdgeSize}.
- */
- public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
- int displayHeight) {
- final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
- final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
-
- final int width;
- final int height;
- if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
- // Beyond these points, we can just use the min size as the shorter edge
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- } else {
- // Within these points, we ensure that the bounds fit within the radius of the limits
- // at the points
- final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
- final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
- height = (int) Math.round(Math.sqrt((radius * radius) /
- (aspectRatio * aspectRatio + 1)));
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
- }
-
- /**
- * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
- * minimum edge is at least minEdgeSize.
- */
- public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
- final int smallestSize = Math.min(size.getWidth(), size.getHeight());
- final int minSize = (int) Math.max(minEdgeSize, smallestSize);
-
- final int width;
- final int height;
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size.
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
- }
-
- /**
* Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
* the new bounds out to {@param boundsOut}.
*/
- public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
+ @VisibleForTesting
+ void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
@PipBoundsState.StashType int stashType) {
int leftEdge = stackBounds.left;
if (stashType == STASH_TYPE_LEFT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 1c5d5b8a8262..48fa2115305d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -307,8 +307,7 @@ public class PipTouchHandler {
public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
- toMovementBounds, 0);
+ mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
final int prevBottom = mPipBoundsState.getMovementBounds().bottom
- mMovementBoundsExtraOffsets;
if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
@@ -339,13 +338,13 @@ public class PipTouchHandler {
// Re-calculate the expanded bounds
Rect normalMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds,
normalMovementBounds, bottomOffset);
if (mPipBoundsState.getMovementBounds().isEmpty()) {
// mMovementBounds is not initialized yet and a clean movement bounds without
// bottom offset shall be used later in this function.
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */);
}
@@ -353,12 +352,12 @@ public class PipTouchHandler {
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio(
+ Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
mPipBoundsState.setExpandedBounds(
new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
Rect expandedMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(
+ mPipBoundsAlgorithm.getMovementBounds(
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
@@ -381,7 +380,7 @@ public class PipTouchHandler {
} else {
final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
final Rect toMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
toMovementBounds, mIsImeShowing ? mImeHeight : 0);
final int prevBottom = mPipBoundsState.getMovementBounds().bottom
- mMovementBoundsExtraOffsets;
@@ -659,7 +658,7 @@ public class PipTouchHandler {
private void animateToUnexpandedState(Rect restoreBounds) {
Rect restoredMovementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds,
+ mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */);
@@ -865,7 +864,7 @@ public class PipTouchHandler {
* resized.
*/
private void updateMovementBounds() {
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mPipBoundsState.getBounds(),
+ mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
mMotionHelper.onMovementBoundsChanged();
@@ -877,7 +876,7 @@ public class PipTouchHandler {
private Rect getMovementBounds(Rect curBounds) {
Rect movementBounds = new Rect();
- mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds,
movementBounds, mIsImeShowing ? mImeHeight : 0);
return movementBounds;
}
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
new file mode 100644
index 000000000000..2c6c7b358e3b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -0,0 +1,2 @@
+# includes OWNERS from parent directories
+natanieljr@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index e1c9384f7081..0fb43e263d05 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -92,6 +92,26 @@ fun LayersAssertion.navBarLayerIsAlwaysVisible(
}
@JvmOverloads
+fun LayersAssertion.appPairsDividerIsVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ end("appPairsDividerIsVisible", bugId, enabled) {
+ this.showsLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertion.appPairsDividerIsInvisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ end("appPairsDividerIsInVisible", bugId, enabled) {
+ this.hasNotLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+ }
+}
+
+@JvmOverloads
fun LayersAssertion.dockedStackDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 2e6037d148a8..54b8fdc83a1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -130,6 +130,7 @@ abstract class FlickerTestBase {
const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+ const val APP_PAIRS_DIVIDER = "AppPairDivider"
const val IMAGE_WALLPAPER = "ImageWallpaper"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
new file mode 100644
index 000000000000..c33dbbc3f53a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.util.Log
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS
+import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+import java.io.IOException
+
+/**
+ * Test AppPairs launch.
+ * To run this test: `atest WMShellFlickerTests:AppPairsTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTest(
+ rotationName: String,
+ rotation: Int
+) : AppPairsTestBase(rotationName, rotation) {
+ private val appPairsSetup: FlickerBuilder
+ get() = FlickerBuilder(instrumentation).apply {
+ val testLaunchActivity = "launch_appPairs_primary_secondary_activities"
+ withTestName {
+ testLaunchActivity
+ }
+ setup {
+ eachRun {
+ uiDevice.wakeUpAndGoToHomeScreen()
+ primaryApp.open()
+ uiDevice.pressHome()
+ secondaryApp.open()
+ uiDevice.pressHome()
+ updateTaskId()
+ }
+ }
+ teardown {
+ eachRun {
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, false /* pair */))
+ primaryApp.forceStop()
+ secondaryApp.forceStop()
+ }
+ }
+ assertions {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
+
+ @Test
+ fun testAppPairs_pairPrimaryAndSecondaryApps() {
+ val testTag = "testAppPaired_pairPrimaryAndSecondary"
+ runWithFlicker(appPairsSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, true /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ assertions {
+ layersTrace {
+ appPairsDividerIsVisible()
+ end("appsEndingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .and()
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(primaryApp.defaultWindowName)
+ .and()
+ .showsAppWindow(secondaryApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testAppPairs_unpairPrimaryAndSecondary() {
+ val testTag = "testAppPairs_unpairPrimaryAndSecondary"
+ runWithFlicker(appPairsSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ setup {
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, true /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, false /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ assertions {
+ layersTrace {
+ appPairsDividerIsInvisible()
+ start("appsStartingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .and()
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ end("appsEndingBounds", enabled = false) {
+ this.hasNotLayer(primaryApp.defaultWindowName)
+ .and()
+ .hasNotLayer(secondaryApp.defaultWindowName)
+ }
+ }
+ windowManagerTrace {
+ end {
+ hidesAppWindow(primaryApp.defaultWindowName)
+ .and()
+ .hidesAppWindow(secondaryApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ private fun composePairsCommand(
+ primaryApp: String,
+ secondaryApp: String,
+ pair: Boolean
+ ): String = buildString {
+ // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2}
+ append("dumpsys activity service SystemUIService WMShell ")
+ if (pair) {
+ append("pair ")
+ } else {
+ append("unpair ")
+ }
+ append(primaryApp + " " + secondaryApp)
+ }
+
+ private fun executeShellCommand(cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.d("AppPairsTest", "executeShellCommand error!" + e)
+ }
+ }
+
+ private fun updateTaskId() {
+ val primaryAppComponent = primaryApp.openAppIntent.component
+ val secondaryAppComponent = secondaryApp.openAppIntent.component
+ if (primaryAppComponent != null) {
+ primaryTaskId = appPairsHelper.getTaskIdForActivity(
+ primaryAppComponent.packageName, primaryAppComponent.className).toString()
+ }
+ if (secondaryAppComponent != null) {
+ secondaryTaskId = appPairsHelper.getTaskIdForActivity(
+ secondaryAppComponent.packageName, secondaryAppComponent.className).toString()
+ }
+ }
+
+ companion object {
+ var primaryTaskId = ""
+ var secondaryTaskId = ""
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
new file mode 100644
index 000000000000..1a4de0a80bec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import com.android.wm.shell.flicker.NonRotationTestBase
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class AppPairsTestBase(
+ rotationName: String,
+ rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+ protected val appPairsHelper = AppPairsHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+ protected val primaryApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+ protected val secondaryApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_SECONDARY_LABEL,
+ TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
new file mode 100644
index 000000000000..3b6fcdbee4be
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.graphics.Region
+import android.system.helpers.ActivityHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+class AppPairsHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentName: ComponentName
+) : BaseAppHelper(
+ instrumentation,
+ activityLabel,
+ componentName
+) {
+ val activityHelper = ActivityHelper.getInstance()
+
+ fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+ val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+ dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
+ return primaryAppBounds
+ }
+
+ fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+ val displayBounds = WindowUtils.displayBounds
+ val secondaryAppBounds = Region(0,
+ dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+ return secondaryAppBounds
+ }
+
+ fun getTaskIdForActivity(pkgName: String, activityName: String): Int {
+ return activityHelper.getTaskIdForActivity(pkgName, activityName)
+ }
+
+ companion object {
+ const val TEST_REPETITIONS = 1
+ const val TIMEOUT_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index d343f2a7093b..3813d4dc3700 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -76,12 +76,12 @@ class PipKeyboardTest(
}
teardown {
test {
- keyboardApp.exit()
+ keyboardApp.forceStop()
if (device.hasPipWindow()) {
device.closePipWindow()
}
- testApp.exit()
+ testApp.forceStop()
this.setRotation(Surface.ROTATION_0)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 7a6e0c1b41fc..a65d832359d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -50,6 +50,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
private static final float DEFAULT_ASPECT_RATIO = 1f;
private static final float MIN_ASPECT_RATIO = 0.5f;
private static final float MAX_ASPECT_RATIO = 2f;
+ private static final int DEFAULT_MIN_EDGE_SIZE = 100;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private DisplayInfo mDefaultDisplayInfo;
@@ -73,7 +74,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
com.android.internal.R.integer.config_defaultPictureInPictureGravity,
Gravity.END | Gravity.BOTTOM);
res.addOverride(
- com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100);
+ com.android.internal.R.dimen.default_minimal_size_pip_resizable_task,
+ DEFAULT_MIN_EDGE_SIZE);
res.addOverride(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
"16x16");
@@ -112,6 +114,127 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
}
@Test
+ public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
+ final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
+ DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
+ mDefaultDisplayInfo.logicalHeight);
+
+ mPipBoundsState.setOverrideMinSize(null);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height()));
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ }
+
+ @Test
+ public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() {
+ overrideDefaultAspectRatio(1.0f);
+ // The min size's aspect ratio is greater than the default aspect ratio.
+ final Size overrideMinSize = new Size(150, 120);
+
+ mPipBoundsState.setOverrideMinSize(overrideMinSize);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // The default aspect ratio should trump the min size aspect ratio.
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ // The width of the min size is still used with the default aspect ratio.
+ assertEquals(overrideMinSize.getWidth(), defaultBounds.width());
+ }
+
+ @Test
+ public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() {
+ overrideDefaultAspectRatio(1.0f);
+ // The min size's aspect ratio is greater than the default aspect ratio.
+ final Size overrideMinSize = new Size(120, 150);
+
+ mPipBoundsState.setOverrideMinSize(overrideMinSize);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // The default aspect ratio should trump the min size aspect ratio.
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ // The height of the min size is still used with the default aspect ratio.
+ assertEquals(overrideMinSize.getHeight(), defaultBounds.height());
+ }
+
+ @Test
+ public void getDefaultBounds_imeShowing_offsetByImeHeight() {
+ final int imeHeight = 30;
+ mPipBoundsState.setImeVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setImeVisibility(true, imeHeight);
+ final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top);
+ }
+
+ @Test
+ public void getDefaultBounds_shelfShowing_offsetByShelfHeight() {
+ final int shelfHeight = 30;
+ mPipBoundsState.setShelfVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setShelfVisibility(true, shelfHeight);
+ final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top);
+ }
+
+ @Test
+ public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() {
+ final int imeHeight = 30;
+ final int shelfHeight = 40;
+ mPipBoundsState.setImeVisibility(false, 0);
+ mPipBoundsState.setShelfVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setImeVisibility(true, imeHeight);
+ mPipBoundsState.setShelfVisibility(true, shelfHeight);
+ final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top);
+ }
+
+ @Test
+ public void getDefaultBounds_boundsAtDefaultGravity() {
+ final Rect insetBounds = new Rect();
+ mPipBoundsAlgorithm.getInsetBounds(insetBounds);
+ overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM);
+
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(insetBounds.bottom, defaultBounds.bottom);
+ assertEquals(insetBounds.right, defaultBounds.right);
+ }
+
+ @Test
+ public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() {
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // Set an invalid current aspect ratio.
+ mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2);
+ final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+ assertEquals(defaultBounds, normalBounds);
+ }
+
+ @Test
+ public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() {
+ final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds();
+ mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio,
+ MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
+
+ // Set a valid current aspect ratio different that the default.
+ mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO);
+ final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+ assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds);
+ }
+
+ @Test
public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() {
final float[] aspectRatios = new float[] {
(MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
@@ -121,8 +244,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
for (float aspectRatio : aspectRatios) {
mPipBoundsState.setAspectRatio(aspectRatio);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- final float actualAspectRatio =
- destinationBounds.width() / (destinationBounds.height() * 1f);
+ final float actualAspectRatio = getRectAspectRatio(destinationBounds);
assertEquals("Destination bounds matches the given aspect ratio",
aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
}
@@ -274,6 +396,22 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
+ private void overrideDefaultAspectRatio(float aspectRatio) {
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ aspectRatio);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ }
+
+ private void overrideDefaultStackGravity(int stackGravity) {
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+ stackGravity);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ }
+
private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
final Rect expectedWithMargin = new Rect(expected);
expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
@@ -282,4 +420,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
+ " with error margin " + ROUNDING_ERROR_MARGIN,
expectedWithMargin.contains(actual));
}
+
+ private static float getRectAspectRatio(Rect rect) {
+ return rect.width() / (rect.height() * 1f);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
new file mode 100644
index 000000000000..dcee2e1847b2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link PipSnapAlgorithm}. **/
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipSnapAlgorithmTest extends ShellTestCase {
+ private static final int DEFAULT_STASH_OFFSET = 32;
+ private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000);
+ private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100);
+ private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0,
+ DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(),
+ DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width());
+
+ private PipSnapAlgorithm mPipSnapAlgorithm;
+
+ @Before
+ public void setUp() {
+ mPipSnapAlgorithm = new PipSnapAlgorithm();
+ }
+
+ @Test
+ public void testApplySnapFraction_topEdge() {
+ final float snapFraction = 0.25f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_rightEdge() {
+ final float snapFraction = 1.5f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_bottomEdge() {
+ final float snapFraction = 2.25f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_leftEdge() {
+ final float snapFraction = 3.75f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_notStashed_isNotOffBounds() {
+ final float snapFraction = 2f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_stashedLeft() {
+ final float snapFraction = 3f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET;
+ assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_stashedRight() {
+ final float snapFraction = 2f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_rightEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the right side.
+ bounds.offset(10, 0);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_leftEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the left side.
+ bounds.offset(-10, 0);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_topEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the top half.
+ bounds.offset(0, -10);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_bottomEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the bottom half.
+ bounds.offset(0, 10);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_stashed_unStahesBounds() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Stash it on the left side.
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f,
+ PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_LEFT);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ }
+
+ @Test
+ public void testGetSnapFraction_leftEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the left side.
+ bounds.offset(-10, 0);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(3.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_rightEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the right side.
+ bounds.offset(10, 0);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(1.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_topEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the top half.
+ bounds.offset(0, -10);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(0.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_bottomEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the bottom half.
+ bounds.offset(0, 10);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(2.5f, snapFraction, 0.1f);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index b25c74d12818..abbc681f53fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -91,7 +91,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipBoundsState = new PipBoundsState(mContext);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
- mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+ mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
mFloatingContentCoordinator, mPipUiEventLogger);
@@ -115,7 +115,8 @@ public class PipTouchHandlerTest extends ShellTestCase {
@Test
public void updateMovementBounds_minBounds() {
Rect expectedMinMovementBounds = new Rect();
- mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+ mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds,
+ 0);
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
@@ -129,12 +130,13 @@ public class PipTouchHandlerTest extends ShellTestCase {
public void updateMovementBounds_maxBounds() {
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+ Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1,
mContext.getResources().getDimensionPixelSize(
R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
Rect expectedMaxMovementBounds = new Rect();
- mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+ mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds,
+ 0);
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 27b33ace532a..a9da77230214 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -878,11 +878,14 @@ public class Tuner implements AutoCloseable {
}
/**
- * Connects Conditional Access Modules (CAM) through Common Interface (CI)
+ * Connects Conditional Access Modules (CAM) through Common Interface (CI).
*
* <p>The demux uses the output from the frontend as the input by default, and must change to
* use the output from CI-CAM as the input after this call.
*
+ * <p> Note that this API is used to connect the CI-CAM to the Demux module while
+ * {@link connectFrontendToCiCam(int)} is used to connect CI-CAM to the Frontend module.
+ *
* @param ciCamId specify CI-CAM Id to connect.
* @return result status of the operation.
*/
@@ -895,23 +898,30 @@ public class Tuner implements AutoCloseable {
}
/**
- * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode.
+ * Connect Conditional Access Modules (CAM) Frontend to support Common Interface (CI)
+ * by-pass mode.
*
* <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that
* the CICAM also receives the TS concurrently from the frontend when the Demux is receiving
* the TS directly from the frontend.
*
- * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect.
+ * <p> Note that this API is used to connect the CI-CAM to the Frontend module while
+ * {@link connectCiCam(int)} is used to connect CI-CAM to the Demux module.
+ *
+ * <p>Use {@link #disconnectFrontendToCiCam(int)} to disconnect.
*
* <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
* no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to
* check the version.
*
- * @param ciCamId specify CI-CAM Id to link.
+ * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
+ * Common Interface (CI), to link.
* @return Local transport stream id when connection is successfully established. Failed
- * operation returns {@link INVALID_LTS_ID}.
+ * operation returns {@link INVALID_LTS_ID} while unsupported version also returns
+ * {@link INVALID_LTS_ID}. Check the current HAL version using
+ * {@link TunerVersionChecker.getTunerVersion()}.
*/
- public int linkFrontendToCiCam(int ciCamId) {
+ public int connectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"linkFrontendToCiCam")) {
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
@@ -922,10 +932,13 @@ public class Tuner implements AutoCloseable {
}
/**
- * Disconnects Conditional Access Modules (CAM)
+ * Disconnects Conditional Access Modules (CAM).
*
* <p>The demux will use the output from the frontend as the input after this call.
*
+ * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
+ * {@link disconnectFrontendToCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
+ *
* @return result status of the operation.
*/
@Result
@@ -937,18 +950,23 @@ public class Tuner implements AutoCloseable {
}
/**
- * Unlink Conditional Access Modules (CAM) Frontend.
+ * Disconnect Conditional Access Modules (CAM) Frontend.
*
* <p>It is used by the client to unlink CI-CAM to a Frontend.
*
+ * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
+ * {@link disconnectCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
+ *
* <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
* no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
*
- * @param ciCamId specify CI-CAM Id to unlink.
- * @return result status of the operation.
+ * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
+ * Common Interface (CI), to disconnect.
+ * @return result status of the operation. Unsupported version would return
+ * {@link RESULT_UNAVAILABLE}
*/
@Result
- public int unlinkFrontendToCiCam(int ciCamId) {
+ public int disconnectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"unlinkFrontendToCiCam")) {
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 82cc78d7e47d..451f54caf7f9 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -186,7 +186,7 @@ public class Filter implements AutoCloseable {
value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED,
SCRAMBLING_STATUS_SCRAMBLED})
@Retention(RetentionPolicy.SOURCE)
- public @interface ScramblingStatusMask {}
+ public @interface ScramblingStatus {}
/**
* Content’s scrambling status is unknown
@@ -204,6 +204,23 @@ public class Filter implements AutoCloseable {
public static final int SCRAMBLING_STATUS_SCRAMBLED =
android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED;
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "MONITOR_EVENT_",
+ value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MonitorEventTypeMask {}
+
+ /**
+ * Monitor scrambling status change.
+ */
+ public static final int MONITOR_EVENT_SCRAMBLING_STATUS =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS;
+ /**
+ * Monitor ip cid change.
+ */
+ public static final int MONITOR_EVENT_IP_CID_CHANGE =
+ android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE;
private static final String TAG = "Filter";
@@ -222,7 +239,7 @@ public class Filter implements AutoCloseable {
int type, int subType, FilterConfiguration settings);
private native int nativeGetId();
private native long nativeGetId64Bit();
- private native int nativeconfigureScramblingEvent(int scramblingStatusMask);
+ private native int nativeConfigureMonitorEvent(int monitorEventTypesMask);
private native int nativeSetDataSource(Filter source);
private native int nativeStartFilter();
private native int nativeStopFilter();
@@ -306,34 +323,40 @@ public class Filter implements AutoCloseable {
}
/**
- * Configure the Filter to monitor specific Scrambling Status through
- * {@link ScramblingStatusEvent}.
+ * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit of
+ * {@link MonitorEventTypeMask} to monitor the change. Reset to stop monitoring.
*
* <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios:
+ * <ul>
+ * <li>When this method is called with {@link MONITOR_EVENT_SCRAMBLING_STATUS}, the first
+ * detected scrambling status should be sent.
+ * <li>When the Scrambling status transits into different status, event should be sent.
+ * <ul/>
*
+ * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios:
* <ul>
- * <li>When this method is called, the first detected scrambling status should be sent.
- * <li>When the filter transits into the monitored statuses configured in
- * {@code scramblingStatusMask}, event should be sent.
+ * <li>When this method is called with {@link MONITOR_EVENT_IP_CID_CHANGE}, the first detected
+ * CID for the IP should be sent.
+ * <li>When the CID is changed to different value for the IP filter, event should be sent.
* <ul/>
*
* <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version
* will cause no-op. Use {@link TunerVersionChecker.getTunerVersion()} to get the version
* information.
*
- * @param scramblingStatusMask Scrambling Statuses to be monitored. Set corresponding bit to
- * monitor it. Reset to stop monitoring.
+ * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to
+ * monitor it. Reset to stop monitoring.
* @return result status of the operation.
*/
@Result
- public int configureScramblingStatusEvent(@ScramblingStatusMask int scramblingStatusMask) {
+ public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_1_1, "configureScramblingStatusEvent")) {
+ TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) {
return Tuner.RESULT_UNAVAILABLE;
}
- return nativeconfigureScramblingEvent(scramblingStatusMask);
+ return nativeConfigureMonitorEvent(monitorEventTypesMask);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java
new file mode 100644
index 000000000000..c0043f3b15cd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.SystemApi;
+
+/**
+ * Ip Cid Change event sent from {@link Filter} objects new ip cid.
+ *
+ * <p>This event is only sent in Tuner 1.1 or higher version. Use
+ * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
+ * @hide
+ */
+@SystemApi
+public final class IpCidChangeEvent extends FilterEvent {
+ private final int mCid;
+
+ private IpCidChangeEvent(int cid) {
+ mCid = cid;
+ }
+
+ /**
+ * Gets ip cid.
+ *
+ * <p>This event is only sent in Tuner 1.1 or higher version. Use
+ * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
+ */
+ public int getIpCid() {
+ return mCid;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
index a78b96e9cf51..fef539638db8 100644
--- a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
+++ b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java
@@ -30,18 +30,17 @@ import android.annotation.SystemApi;
public final class ScramblingStatusEvent extends FilterEvent {
private final int mScramblingStatus;
- private ScramblingStatusEvent(@Filter.ScramblingStatusMask int scramblingStatus) {
+ private ScramblingStatusEvent(@Filter.ScramblingStatus int scramblingStatus) {
mScramblingStatus = scramblingStatus;
}
/**
* Gets Scrambling Status Type.
*
- * <p>This event field is only sent in Tuner 1.1 or higher version. Unsupported version returns
- * default value 0. Use {@link TunerVersionChecker.getTunerVersion()} to get the version
- * information.
+ * <p>This event field is only sent in Tuner 1.1 or higher version. Use
+ * {@link TunerVersionChecker.getTunerVersion()} to get the version information.
*/
- @Filter.ScramblingStatusMask
+ @Filter.ScramblingStatus
public int getScramblingStatus() {
return mScramblingStatus;
}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index ed56b4398c22..798bf6e2f8ee 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -19,8 +19,7 @@ package android.mtp;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
-import android.content.ContentUris;
-import android.content.ContentValues;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,7 +31,6 @@ import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.BatteryManager;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable {
private MtpStorageManager mManager;
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
- private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID};
- private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA};
private static final String NO_MEDIA = ".nomedia";
static {
@@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable {
}
// Add the new file to MediaProvider
if (succeeded) {
- MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+ updateMediaStore(mContext, obj.getPath().toFile());
}
}
@@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
- // finally update MediaProvider
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, newPath.toString());
- String[] whereArgs = new String[]{oldPath.toString()};
- try {
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
- mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in mMediaProvider.update", e);
- }
-
- // check if nomedia status changed
- if (obj.isDir()) {
- // for directories, check if renamed from something hidden to something non-hidden
- if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
- MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile());
- }
- } else {
- // for files, check if renamed from .nomedia to something else
- if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
- && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
- MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile());
- }
- }
+ updateMediaStore(mContext, oldPath.toFile());
+ updateMediaStore(mContext, newPath.toFile());
return MtpConstants.RESPONSE_OK;
}
@@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable {
Log.e(TAG, "Failed to end move object");
return;
}
-
obj = mManager.getObject(objId);
if (!success || obj == null)
return;
- // Get parent info from MediaProvider, since the id is different from MTP's
- ContentValues values = new ContentValues();
+
Path path = newParentObj.getPath().resolve(name);
Path oldPath = oldParentObj.getPath().resolve(name);
- values.put(Files.FileColumns.DATA, path.toString());
- if (obj.getParent().isRoot()) {
- values.put(Files.FileColumns.PARENT, 0);
- } else {
- int parentId = findInMedia(newParentObj, path.getParent());
- if (parentId != -1) {
- values.put(Files.FileColumns.PARENT, parentId);
- } else {
- // The new parent isn't in MediaProvider, so delete the object instead
- deleteFromMedia(obj, oldPath, obj.isDir());
- return;
- }
- }
- // update MediaProvider
- Cursor c = null;
- String[] whereArgs = new String[]{oldPath.toString()};
- try {
- int parentId = -1;
- if (!oldParentObj.isRoot()) {
- parentId = findInMedia(oldParentObj, oldPath.getParent());
- }
- if (oldParentObj.isRoot() || parentId != -1) {
- // Old parent exists in MediaProvider - perform a move
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
- mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
- } else {
- // Old parent doesn't exist - add the object
- MediaStore.scanFile(mContext.getContentResolver(), path.toFile());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in mMediaProvider.update", e);
- }
+
+ updateMediaStore(mContext, oldPath.toFile());
+ updateMediaStore(mContext, path.toFile());
}
@VisibleForNative
@@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable {
if (!success) {
return;
}
- MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+
+ updateMediaStore(mContext, obj.getPath().toFile());
+ }
+
+ private static void updateMediaStore(@NonNull Context context, @NonNull File file) {
+ final ContentResolver resolver = context.getContentResolver();
+ // For file, check whether the file name is .nomedia or not.
+ // If yes, scan the parent directory to update all files in the directory.
+ if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) {
+ MediaStore.scanFile(resolver, file.getParentFile());
+ } else {
+ MediaStore.scanFile(resolver, file);
+ }
}
@VisibleForNative
@@ -928,26 +879,6 @@ public class MtpDatabase implements AutoCloseable {
deleteFromMedia(obj, obj.getPath(), obj.isDir());
}
- private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
-
- int ret = -1;
- Cursor c = null;
- try {
- c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
- new String[]{path.toString()}, null, null);
- if (c != null && c.moveToNext()) {
- ret = c.getInt(0);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error finding " + path + " in MediaProvider");
- } finally {
- if (c != null)
- c.close();
- }
- return ret;
- }
-
private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
try {
@@ -963,13 +894,10 @@ public class MtpDatabase implements AutoCloseable {
}
String[] whereArgs = new String[]{path.toString()};
- if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
- if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
- MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile());
- }
- } else {
- Log.i(TAG, "Mediaprovider didn't delete " + path);
+ if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) {
+ Log.i(TAG, "MediaProvider didn't delete " + path);
}
+ updateMediaStore(mContext, path.toFile());
} catch (Exception e) {
Log.d(TAG, "Failed to delete " + path + " from MediaProvider");
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index b255a48d34d0..72dc32e7998f 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -604,11 +604,16 @@ jobjectArray FilterCallback::getTsRecordEvent(
jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
- jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts)
- : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
- jlong firstMbInSlice = (eventsExt.size() > i)
- ? static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice)
- : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+ jlong pts;
+ jlong firstMbInSlice;
+ if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+ DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) {
+ pts = static_cast<jlong>(eventsExt[i].tsRecord().pts);
+ firstMbInSlice = static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice);
+ } else {
+ pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+ firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+ }
jobject obj =
env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber,
@@ -632,16 +637,25 @@ jobjectArray FilterCallback::getMmtpRecordEvent(
jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
- jint mpuSequenceNumber = (eventsExt.size() > i)
- ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber)
- : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
- jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts)
- : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
- jlong firstMbInSlice = (eventsExt.size() > i)
- ? static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice)
- : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
- jlong tsIndexMask = (eventsExt.size() > i)
- ? static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask) : 0;
+
+ jint mpuSequenceNumber;
+ jlong pts;
+ jlong firstMbInSlice;
+ jlong tsIndexMask;
+
+ if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
+ DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) {
+ mpuSequenceNumber = static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber);
+ pts = static_cast<jlong>(eventsExt[i].mmtpRecord().pts);
+ firstMbInSlice = static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice);
+ tsIndexMask = static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask);
+ } else {
+ mpuSequenceNumber =
+ static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
+ pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+ firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
+ tsIndexMask = 0;
+ }
jobject obj =
env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
@@ -720,11 +734,21 @@ jobjectArray FilterCallback::getScramblingStatusEvent(
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- for (int i = 0; i < eventsExt.size(); i++) {
- auto scramblingStatus = eventsExt[i].scramblingStatus();
- jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus));
- env->SetObjectArrayElement(arr, i, obj);
- }
+ auto scramblingStatus = eventsExt[0].monitorEvent().scramblingStatus();
+ jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus));
+ env->SetObjectArrayElement(arr, 0, obj);
+ return arr;
+}
+
+jobjectArray FilterCallback::getIpCidChangeEvent(
+ jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
+
+ auto cid = eventsExt[0].monitorEvent().cid();
+ jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(cid));
+ env->SetObjectArrayElement(arr, 0, obj);
return arr;
}
@@ -734,11 +758,9 @@ jobjectArray FilterCallback::getRestartEvent(
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- for (int i = 0; i < eventsExt.size(); i++) {
- auto startId = eventsExt[i].startId();
- jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId));
- env->SetObjectArrayElement(arr, i, obj);
- }
+ auto startId = eventsExt[0].startId();
+ jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId));
+ env->SetObjectArrayElement(arr, 0, obj);
return arr;
}
@@ -747,17 +769,31 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve
ALOGD("FilterCallback::onFilterEvent_1_1");
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobjectArray array;
std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
- jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL);
if (events.empty() && !eventsExt.empty()) {
+ // Monitor event should be sent with one DemuxFilterMonitorEvent in DemuxFilterEventExt.
+ array = env->NewObjectArray(1, eventClazz, NULL);
auto eventExt = eventsExt[0];
switch (eventExt.getDiscriminator()) {
- case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus: {
- array = getScramblingStatusEvent(array, eventsExt);
+ case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: {
+ switch (eventExt.monitorEvent().getDiscriminator()) {
+ case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: {
+ array = getScramblingStatusEvent(array, eventsExt);
+ break;
+ }
+ case DemuxFilterMonitorEvent::hidl_discriminator::cid: {
+ array = getIpCidChangeEvent(array, eventsExt);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
break;
}
case DemuxFilterEventExt::Event::hidl_discriminator::startId: {
@@ -771,6 +807,7 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve
}
if (!events.empty()) {
+ array = env->NewObjectArray(events.size(), eventClazz, NULL);
auto event = events[0];
switch (event.getDiscriminator()) {
case DemuxFilterEvent::Event::hidl_discriminator::media: {
@@ -4069,8 +4106,8 @@ static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject fil
::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
}
-static jint android_media_tv_Tuner_configure_scrambling_status_event(
- JNIEnv* env, jobject filter, int scramblingStatusMask) {
+static jint android_media_tv_Tuner_configure_monitor_event(
+ JNIEnv* env, jobject filter, int monitorEventType) {
sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
if (iFilterSp == NULL) {
ALOGD("Failed to configure scrambling event: filter not found");
@@ -4082,7 +4119,7 @@ static jint android_media_tv_Tuner_configure_scrambling_status_event(
Result res;
if (iFilterSp_1_1 != NULL) {
- res = iFilterSp_1_1->configureScramblingEvent(scramblingStatusMask);
+ res = iFilterSp_1_1->configureMonitorEvent(monitorEventType);
} else {
ALOGW("configureScramblingEvent is not supported with the current HAL implementation.");
return (jint) Result::INVALID_STATE;
@@ -4762,8 +4799,8 @@ static const JNINativeMethod gFilterMethods[] = {
{ "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
{ "nativeGetId64Bit", "()J",
(void *)android_media_tv_Tuner_get_filter_64bit_id },
- { "nativeconfigureScramblingEvent", "(I)I",
- (void *)android_media_tv_Tuner_configure_scrambling_status_event },
+ { "nativeConfigureMonitorEvent", "(I)I",
+ (void *)android_media_tv_Tuner_configure_monitor_event },
{ "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_set_filter_data_source },
{ "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index ebb95f43c52d..e79b5c2e391a 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -44,7 +44,6 @@ using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_vec;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
-using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxPid;
@@ -73,6 +72,8 @@ using ::android::hardware::tv::tuner::V1_0::LnbId;
using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
using ::android::hardware::tv::tuner::V1_0::RecordStatus;
using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
@@ -188,6 +189,8 @@ private:
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
jobjectArray getScramblingStatusEvent(
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
+ jobjectArray getIpCidChangeEvent(
+ jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
jobjectArray getRestartEvent(
jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
};
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index d134b37c6b70..eca67bd7d211 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -151,6 +151,7 @@ LIBANDROID {
AHardwareBuffer_allocate; # introduced=26
AHardwareBuffer_describe; # introduced=26
AHardwareBuffer_fromHardwareBuffer; # introduced=26
+ AHardwareBuffer_getId; # introduced=31
AHardwareBuffer_getNativeHandle; # introduced=26
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock; # introduced=26
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index cea7903b8726..9f1c96d88905 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1109,6 +1109,8 @@
<string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until charged</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited -->
+ <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d16aebb4d4b4..9331b5e09f38 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -269,6 +269,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_GESTURE_CALL_NUMBER, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a3bda96337bb..af12ddd8895b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -315,7 +315,6 @@ public class SettingsBackupTest {
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
Settings.Global.LANG_ID_UPDATE_METADATA_URL,
- Settings.Global.LATENCY_TRACKER,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -743,6 +742,7 @@ public class SettingsBackupTest {
Settings.Secure.SKIP_GESTURE,
Settings.Secure.SILENCE_GESTURE,
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+ Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER,
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
Settings.Secure.FACE_UNLOCK_RE_ENROLL,
Settings.Secure.TAP_GESTURE,
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index f7e9fedd5f66..d95ab374a5cb 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -79,6 +79,9 @@
is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. -->
<string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
+ <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily. -->
+ <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string>
+
<!-- When the lock screen is showing and the battery is low, warn user to plug
in the phone soon. -->
<string name="keyguard_low_battery">Connect your charger.</string>
diff --git a/packages/SystemUI/res/layout/controls_dialog_pin.xml b/packages/SystemUI/res/layout/controls_dialog_pin.xml
index 170b32b6c669..d0ef10b649bf 100644
--- a/packages/SystemUI/res/layout/controls_dialog_pin.xml
+++ b/packages/SystemUI/res/layout/controls_dialog_pin.xml
@@ -27,6 +27,7 @@
android:layout_height="wrap_content"
android:minHeight="48dp"
android:longClickable="false"
+ android:textAlignment="viewStart"
android:inputType="numberPassword" />
<CheckBox
android:id="@+id/controls_pin_use_alpha"
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 283918912c76..f38e653190b0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -305,6 +305,9 @@
<style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
</style>
+ <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod">
+ </style>
+
<!-- Standard animations for hiding and showing the status bar. -->
<style name="Animation.StatusBar">
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
new file mode 100644
index 000000000000..4a870f17ab0a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+
+import java.util.HashMap;
+
+public class ViewTreeObserverWrapper {
+
+ private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
+ sOnComputeInsetsListenerMap = new HashMap<>();
+
+ /**
+ * Register a callback to be invoked when the invoked when it is time to
+ * compute the window's insets.
+ *
+ * @param listener The callback to add
+ * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
+ */
+ public static void addOnComputeInsetsListener(
+ ViewTreeObserver observer, OnComputeInsetsListener listener) {
+ final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
+ final InsetsInfo inOutInfo = new InsetsInfo();
+ inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
+ inOutInfo.visibleInsets.set(internalInOutInfo.visibleInsets);
+ inOutInfo.touchableRegion.set(internalInOutInfo.touchableRegion);
+ listener.onComputeInsets(inOutInfo);
+ internalInOutInfo.contentInsets.set(inOutInfo.contentInsets);
+ internalInOutInfo.visibleInsets.set(inOutInfo.visibleInsets);
+ internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
+ internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
+ };
+ sOnComputeInsetsListenerMap.put(listener, internalListener);
+ observer.addOnComputeInternalInsetsListener(internalListener);
+ }
+
+ /**
+ * Remove a previously installed insets computation callback
+ *
+ * @param victim The callback to remove
+ * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
+ * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
+ */
+ public void removeOnComputeInsetsListener(
+ ViewTreeObserver observer, OnComputeInsetsListener victim) {
+ final OnComputeInternalInsetsListener listener = sOnComputeInsetsListenerMap.get(victim);
+ if (listener != null) {
+ observer.removeOnComputeInternalInsetsListener(listener);
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when layout has
+ * completed and the client can compute its interior insets.
+ */
+ public interface OnComputeInsetsListener {
+ /**
+ * Callback method to be invoked when layout has completed and the
+ * client can compute its interior insets.
+ *
+ * @param inoutInfo Should be filled in by the implementation with
+ * the information about the insets of the window. This is called
+ * with whatever values the previous OnComputeInsetsListener
+ * returned, if there are multiple such listeners in the window.
+ */
+ void onComputeInsets(InsetsInfo inoutInfo);
+ }
+
+ /**
+ * Parameters used with OnComputeInsetsListener.
+ */
+ public final static class InsetsInfo {
+
+ /**
+ * Offsets from the frame of the window at which the content of
+ * windows behind it should be placed.
+ */
+ public final Rect contentInsets = new Rect();
+
+ /**
+ * Offsets from the frame of the window at which windows behind it
+ * are visible.
+ */
+ public final Rect visibleInsets = new Rect();
+
+ /**
+ * Touchable region defined relative to the origin of the frame of the window.
+ * Only used when {@link #setTouchableInsets(int)} is called with
+ * the option {@link #TOUCHABLE_INSETS_REGION}.
+ */
+ public final Region touchableRegion = new Region();
+
+ /**
+ * Option for {@link #setTouchableInsets(int)}: the entire window frame
+ * can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_FRAME =
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+
+ /**
+ * Option for {@link #setTouchableInsets(int)}: the area inside of
+ * the content insets can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_CONTENT =
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+
+ /**
+ * Option for {@link #setTouchableInsets(int)}: the area inside of
+ * the visible insets can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_VISIBLE =
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+
+ /**
+ * Option for {@link #setTouchableInsets(int)}: the area inside of
+ * the provided touchable region in {@link #touchableRegion} can be touched.
+ */
+ public static final int TOUCHABLE_INSETS_REGION =
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+ /**
+ * Set which parts of the window can be touched: either
+ * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
+ * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
+ */
+ public void setTouchableInsets(int val) {
+ mTouchableInsets = val;
+ }
+
+ int mTouchableInsets;
+
+ @Override
+ public int hashCode() {
+ int result = contentInsets.hashCode();
+ result = 31 * result + visibleInsets.hashCode();
+ result = 31 * result + touchableRegion.hashCode();
+ result = 31 * result + mTouchableInsets;
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final InsetsInfo other = (InsetsInfo) o;
+ return mTouchableInsets == other.mTouchableInsets &&
+ contentInsets.equals(other.contentInsets) &&
+ visibleInsets.equals(other.visibleInsets) &&
+ touchableRegion.equals(other.touchableRegion);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index bb7906e7c3ae..714d267bb07d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -23,6 +23,7 @@ import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -94,7 +95,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
} catch (NoSuchMethodException
| IllegalAccessException
| InvocationTargetException e) {
- // no-op
+ Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e);
}
}
);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 2b33f8cd036e..182b3e114887 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -101,7 +101,7 @@ public class AuthContainerView extends LinearLayout
@VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle;
- private @ContainerState int mContainerState = STATE_UNKNOWN;
+ @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason;
@@ -632,10 +632,11 @@ public class AuthContainerView extends LinearLayout
mWindowManager.removeView(this);
}
- private void onDialogAnimatedIn() {
+ @VisibleForTesting
+ void onDialogAnimatedIn() {
if (mContainerState == STATE_PENDING_DISMISS) {
Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
- animateAway(false /* sendReason */, 0);
+ animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
return;
}
mContainerState = STATE_SHOWING;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3fc9f4d2aa61..8776532debcd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -187,12 +187,13 @@ class UdfpsController implements DozeReceiver {
// TODO(b/152419866): Use the UDFPS window type when it becomes available.
WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSLUCENT);
mCoreLayoutParams.setTitle(TAG);
mCoreLayoutParams.setFitInsetsTypes(0);
+ mCoreLayoutParams.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
mView.setSensorProperties(mSensorProps);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 58e49f896931..6888d1ad6063 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -134,7 +134,7 @@ public class DozeTriggers implements DozeMachine.Part {
DOZING_UPDATE_SENSOR_TAP(441),
@UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.")
- DOZING_UPDATE_AUTH_TRIGGERED(442);
+ DOZING_UPDATE_AUTH_TRIGGERED(657);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 37bcb163d6f3..bd3b899adee7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -329,7 +329,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
Method injectMethod = rootComponent.getClass()
.getMethod("inject", getClass());
injectMethod.invoke(rootComponent, this);
- Log.w("TAG", "mMediaManager is now: " + mMediaManager);
+ Log.w(TAG, "mMediaManager is now: " + mMediaManager);
} catch (NoSuchMethodException ex) {
Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
} catch (IllegalAccessException | InvocationTargetException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 78939dffb4fb..8a9a6e694658 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -105,6 +105,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
window.setAttributes(lp);
window.setContentView(mDialogView);
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ window.setWindowAnimations(R.style.Animation_MediaOutputDialog);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a27e9ac61848..8c66ba32ec79 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -16,6 +16,8 @@
package com.android.systemui.power;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -334,10 +336,14 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
private PendingIntent pendingBroadcast(String action) {
- return PendingIntent.getBroadcastAsUser(mContext, 0,
- new Intent(action).setPackage(mContext.getPackageName())
- .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- 0, UserHandle.CURRENT);
+ return PendingIntent.getBroadcastAsUser(
+ mContext,
+ 0 /* request code */,
+ new Intent(action)
+ .setPackage(mContext.getPackageName())
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ FLAG_IMMUTABLE /* flags */,
+ UserHandle.CURRENT);
}
private static Intent settings(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index fbcfef3964af..153ba514fc33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2554,12 +2554,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
- if (child.getVisibility() != View.GONE && !(child instanceof StackScrollerDecorView)
- && child != mShelf) {
+ if (child.getVisibility() != View.GONE
+ && !(child instanceof StackScrollerDecorView)
+ && child != mShelf
+ && !mAmbientState.getDraggedViews().contains(child)) {
children.add(child);
}
}
-
return children;
}
@@ -5524,10 +5525,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
void addDraggedView(View view) {
mAmbientState.onBeginDrag(view);
+ updateFirstAndLastBackgroundViews();
}
void removeDraggedView(View view) {
mAmbientState.onDragFinished(view);
+ updateFirstAndLastBackgroundViews();
}
void setTopHeadsUpEntry(NotificationEntry topEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7cee365bc7d3..f5385050f063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -463,7 +463,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
- mView.addDraggedView(animView);
+ mView.removeDraggedView(animView);
mView.updateContinuousShadowDrawing();
mView.updateContinuousBackgroundDrawing();
if (animView instanceof ExpandableNotificationRow) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 777db952591e..5088a53a2d5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -211,6 +211,16 @@ public class AuthContainerViewTest extends SysuiTestCase {
}
@Test
+ public void testOnDialogAnimatedIn_sendsCancelReason_whenPendingDismiss() {
+ initializeContainer(Authenticators.BIOMETRIC_WEAK);
+ mAuthContainer.mContainerState = AuthContainerView.STATE_PENDING_DISMISS;
+ mAuthContainer.onDialogAnimatedIn();
+ verify(mCallback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq(null) /* credentialAttestation */);
+ }
+
+ @Test
public void testLayoutParams_hasSecureWindowFlag() {
final IBinder windowToken = mock(IBinder.class);
final WindowManager.LayoutParams layoutParams =
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 15bd4dc66ccc..ae024ff6d043 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -332,5 +332,9 @@ message SystemMessage {
// Notify the user that the admin suspended personal apps on the device.
// Package: android
NOTE_PERSONAL_APPS_SUSPENDED = 1003;
+
+ // Notify the user that window magnification is available.
+ // package: android
+ NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 857ac6ae62a9..be7643ecbd4e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -37,6 +37,7 @@ import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
+import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
@@ -561,8 +562,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
detectControlGestures, triggerable, displayId);
} else {
magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
- mAms.getFullScreenMagnificationController(), mAms::onMagnificationScaleChanged,
- detectControlGestures, triggerable, displayId);
+ mAms.getFullScreenMagnificationController(),
+ mAms::onMagnificationScaleChanged, detectControlGestures, triggerable,
+ new WindowMagnificationPromptController(displayContext, mUserId), displayId);
}
return magnificationGestureHandler;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 35481a282701..4c85490a8086 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -80,7 +80,6 @@ import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
@@ -122,6 +121,7 @@ import com.android.server.accessibility.magnification.FullScreenMagnificationCon
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index 5b46cb4ab378..f731c440d5f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -18,11 +18,7 @@ package com.android.server.accessibility.gestures;
import static android.view.MotionEvent.INVALID_POINTER_ID;
-import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
-import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
-import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS;
-import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_START_SWIPE_MS;
import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
import android.content.Context;
@@ -30,7 +26,6 @@ import android.graphics.PointF;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Slog;
-import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -49,9 +44,6 @@ class MultiFingerSwipe extends GestureMatcher {
public static final int RIGHT = 1;
public static final int UP = 2;
public static final int DOWN = 3;
- // This is the calculated movement threshold used track if the user is still
- // moving their finger.
- private final float mGestureDetectionThresholdPixels;
// Buffer for storing points for gesture detection.
private final ArrayList<PointF>[] mStrokeBuffers;
@@ -93,10 +85,7 @@ class MultiFingerSwipe extends GestureMatcher {
mStrokeBuffers = new ArrayList[mTargetFingerCount];
mDirection = direction;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- mGestureDetectionThresholdPixels =
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
- * GESTURE_CONFIRM_CM;
- // Calculate minimum gesture velocity
+ // Calculate gesture sampling interval.
final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH;
final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH;
mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
@@ -150,7 +139,6 @@ class MultiFingerSwipe extends GestureMatcher {
return;
}
mPointerIds[pointerIndex] = pointerId;
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
final float x = rawEvent.getX(actionIndex);
final float y = rawEvent.getY(actionIndex);
@@ -197,7 +185,6 @@ class MultiFingerSwipe extends GestureMatcher {
return;
}
mPointerIds[pointerIndex] = pointerId;
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
final float x = rawEvent.getX(actionIndex);
final float y = rawEvent.getY(actionIndex);
@@ -286,65 +273,42 @@ class MultiFingerSwipe extends GestureMatcher {
Math.abs(x - mBase[pointerIndex].x),
Math.abs(y - mBase[pointerIndex].y));
if (DEBUG) {
- Slog.d(
- getGestureName(),
- "moveDelta:"
- + Double.toString(moveDelta)
- + " mGestureDetectionThreshold: "
- + Float.toString(mGestureDetectionThresholdPixels));
+ Slog.d(getGestureName(), "moveDelta:" + moveDelta);
}
if (getState() == STATE_CLEAR) {
if (moveDelta < (mTargetFingerCount * mTouchSlop)) {
// This still counts as a touch not a swipe.
continue;
- } else if (mStrokeBuffers[pointerIndex].size() == 0) {
- // First, make sure we have the right number of fingers down.
- if (mCurrentFingerCount != mTargetFingerCount) {
- cancelGesture(event, rawEvent, policyFlags);
- return;
- }
- // Then, make sure the pointer is going in the right direction.
- int direction =
- toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
- if (direction != mDirection) {
- cancelGesture(event, rawEvent, policyFlags);
- return;
- } else {
- // This is confirmed to be some kind of swipe so start tracking points.
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
- for (int i = 0; i < mTargetFingerCount; ++i) {
- mStrokeBuffers[i].add(new PointF(mBase[i]));
- }
- }
}
- if (moveDelta > mGestureDetectionThresholdPixels) {
- // Try to cancel if the finger starts to go the wrong way.
- // Note that this only works because this matcher assumes one direction.
- int direction =
- toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
- if (direction != mDirection) {
- cancelGesture(event, rawEvent, policyFlags);
- return;
- }
- // If the pointer has moved more than the threshold,
- // update the stored values.
- mBase[pointerIndex].x = x;
- mBase[pointerIndex].y = y;
- mPreviousGesturePoint[pointerIndex].x = x;
- mPreviousGesturePoint[pointerIndex].y = y;
- if (getState() == STATE_CLEAR) {
- startGesture(event, rawEvent, policyFlags);
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
- }
+ // First, make sure we have the right number of fingers down.
+ if (mCurrentFingerCount != mTargetFingerCount) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // Then, make sure the pointer is going in the right direction.
+ int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // This is confirmed to be some kind of swipe so start tracking points.
+ startGesture(event, rawEvent, policyFlags);
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ mStrokeBuffers[i].add(new PointF(mBase[i]));
+ }
+ } else if (getState() == STATE_GESTURE_STARTED) {
+ // Cancel if the finger starts to go the wrong way.
+ // Note that this only works because this matcher assumes one direction.
+ int direction = toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
}
- }
- if (getState() == STATE_GESTURE_STARTED) {
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
// Sample every 2.5 MM in order to guard against minor variations in path.
mPreviousGesturePoint[pointerIndex].x = x;
mPreviousGesturePoint[pointerIndex].y = y;
mStrokeBuffers[pointerIndex].add(new PointF(x, y));
- cancelAfterPauseThreshold(event, rawEvent, policyFlags);
}
}
}
@@ -379,24 +343,6 @@ class MultiFingerSwipe extends GestureMatcher {
}
/**
- * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
- * transitioned to STATE_GESTURE_STARTED the delay is longer.
- */
- private void cancelAfterPauseThreshold(
- MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelPendingTransitions();
- switch (getState()) {
- case STATE_CLEAR:
- cancelAfter(MAX_TIME_TO_START_SWIPE_MS, event, rawEvent, policyFlags);
- break;
- case STATE_GESTURE_STARTED:
- cancelAfter(MAX_TIME_TO_CONTINUE_SWIPE_MS, event, rawEvent, policyFlags);
- break;
- default:
- break;
- }
- }
- /**
* Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions
* to the complete or cancel state depending on the result.
*/
@@ -502,8 +448,6 @@ class MultiFingerSwipe extends GestureMatcher {
if (getState() != STATE_GESTURE_CANCELED) {
builder.append(", mBase: ")
.append(mBase.toString())
- .append(", mGestureDetectionThreshold:")
- .append(mGestureDetectionThresholdPixels)
.append(", mMinPixelsBetweenSamplesX:")
.append(mMinPixelsBetweenSamplesX)
.append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d50e9d720861..efb9d87a0bfd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
private final ScreenStateReceiver mScreenStateReceiver;
+ private final WindowMagnificationPromptController mPromptController;
/**
* {@code true} if this detector should detect and respond to triple-tap
@@ -178,6 +179,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
ScaleChangedListener listener,
boolean detectTripleTap,
boolean detectShortcutTrigger,
+ @NonNull WindowMagnificationPromptController promptController,
int displayId) {
super(listener);
if (DEBUG_ALL) {
@@ -186,6 +188,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
+ ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
}
mFullScreenMagnificationController = fullScreenMagnificationController;
+ mPromptController = promptController;
mDisplayId = displayId;
mDelegatingState = new DelegatingState();
@@ -195,7 +198,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
-
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
mScreenStateReceiver.register();
@@ -264,6 +266,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (mScreenStateReceiver != null) {
mScreenStateReceiver.unregister();
}
+ mPromptController.onDestroy();
// Check if need to reset when MagnificationGestureHandler is the last magnifying service.
mFullScreenMagnificationController.resetAllIfNeeded(
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -278,6 +281,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
} else {
+ mPromptController.showNotificationIfNeeded();
mDetectingState.toggleShortcutTriggered();
}
}
@@ -950,6 +954,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
zoomOff();
} else {
+ mPromptController.showNotificationIfNeeded();
zoomOn(up.getX(), up.getY());
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
new file mode 100644
index 000000000000..721220729a33
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE;
+
+import android.Manifest;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.app.ActivityOptions;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.notification.SystemNotificationChannels;
+
+/**
+ * A class to show notification to prompt the user that this feature is available.
+ */
+public class WindowMagnificationPromptController {
+
+ private static final Uri MAGNIFICATION_WINDOW_MODE_PROMPT_URI = Settings.Secure.getUriFor(
+ ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT);
+ @VisibleForTesting
+ static final String ACTION_DISMISS =
+ "com.android.server.accessibility.magnification.action.DISMISS";
+ @VisibleForTesting
+ static final String ACTION_TURN_ON_IN_SETTINGS =
+ "com.android.server.accessibility.magnification.action.TURN_ON_IN_SETTINGS";
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final ContentObserver mContentObserver;
+ private final int mUserId;
+ @VisibleForTesting
+ BroadcastReceiver mNotificationActionReceiver;
+
+ private boolean mNeedToShowNotification;
+
+ @MainThread
+ public WindowMagnificationPromptController(@NonNull Context context, int userId) {
+ mContext = context;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+ mUserId = userId;
+ mContentObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ onPromptSettingsValueChanged();
+ }
+ };
+ context.getContentResolver().registerContentObserver(MAGNIFICATION_WINDOW_MODE_PROMPT_URI,
+ false, mContentObserver, mUserId);
+ mNeedToShowNotification = isWindowMagnificationPromptEnabled();
+ }
+
+ @VisibleForTesting
+ protected void onPromptSettingsValueChanged() {
+ final boolean needToShowNotification = isWindowMagnificationPromptEnabled();
+ if (mNeedToShowNotification == needToShowNotification) {
+ return;
+ }
+ mNeedToShowNotification = needToShowNotification;
+ if (!mNeedToShowNotification) {
+ unregisterReceiverIfNeeded();
+ mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ }
+ }
+
+ /**
+ * Shows the prompt notification that could bring users to magnification settings if necessary.
+ */
+ @MainThread
+ void showNotificationIfNeeded() {
+ if (!mNeedToShowNotification) return;
+
+ final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+ SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION);
+ notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp)
+ .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title))
+ .setContentText(mContext.getString(R.string.window_magnification_prompt_content))
+ .setLargeIcon(Icon.createWithResource(mContext,
+ R.drawable.ic_accessibility_magnification))
+ .setTicker(mContext.getString(R.string.window_magnification_prompt_title))
+ .setOnlyAlertOnce(true)
+ .setDeleteIntent(createPendingIntent(ACTION_DISMISS))
+ .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS))
+ .setActions(buildTurnOnAction(), buildDismissAction());
+ mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE,
+ notificationBuilder.build());
+ registerReceiverIfNeeded();
+ }
+
+ /**
+ * Called when this object is not used anymore to release resources if necessary.
+ */
+ @VisibleForTesting
+ @MainThread
+ public void onDestroy() {
+ dismissNotification();
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ private boolean isWindowMagnificationPromptEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId) == 1;
+ }
+
+ private Notification.Action buildTurnOnAction() {
+ return new Notification.Action.Builder(null,
+ mContext.getString(R.string.turn_on_magnification_settings_action),
+ createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build();
+ }
+
+ private Notification.Action buildDismissAction() {
+ return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action),
+ createPendingIntent(ACTION_DISMISS)).build();
+ }
+
+ private PendingIntent createPendingIntent(String action) {
+ final Intent intent = new Intent(action);
+ intent.setPackage(mContext.getPackageName());
+ return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ private void registerReceiverIfNeeded() {
+ if (mNotificationActionReceiver != null) {
+ return;
+ }
+ mNotificationActionReceiver = new NotificationActionReceiver();
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_DISMISS);
+ intentFilter.addAction(ACTION_TURN_ON_IN_SETTINGS);
+ mContext.registerReceiver(mNotificationActionReceiver, intentFilter,
+ Manifest.permission.MANAGE_ACCESSIBILITY, null);
+ }
+
+ private void launchMagnificationSettings() {
+ final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+ MAGNIFICATION_COMPONENT_NAME.flattenToShortString());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
+ mContext.getDisplayId()).toBundle();
+ mContext.startActivityAsUser(intent, bundle, UserHandle.of(mUserId));
+ mContext.getSystemService(StatusBarManager.class).collapsePanels();
+ }
+
+ private void dismissNotification() {
+ unregisterReceiverIfNeeded();
+ mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ }
+
+ private void unregisterReceiverIfNeeded() {
+ if (mNotificationActionReceiver == null) {
+ return;
+ }
+ mContext.unregisterReceiver(mNotificationActionReceiver);
+ mNotificationActionReceiver = null;
+ }
+
+ private class NotificationActionReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (TextUtils.isEmpty(action)) return;
+
+ mNeedToShowNotification = false;
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId);
+
+ if (ACTION_TURN_ON_IN_SETTINGS.equals(action)) {
+ launchMagnificationSettings();
+ dismissNotification();
+ } else if (ACTION_DISMISS.equals(action)) {
+ dismissNotification();
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index e32324941aef..5fedf9f89fc1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -62,7 +62,6 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
@@ -88,6 +87,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4bde31fc2f05..3a137263d182 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -61,6 +61,7 @@ genrule {
java_library_static {
name: "services.core.unboosted",
+ defaults: ["platform_service_defaults"],
srcs: [
":services.core.protologsrc",
":dumpstate_aidl",
@@ -146,7 +147,6 @@ java_genrule {
java_library {
name: "services.core",
- defaults: ["platform_service_defaults"],
static_libs: ["services.core.priorityboosted"],
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 9455051ad740..bb4bbd5bc6d4 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -437,6 +437,10 @@ public final class BatteryService extends SystemService {
info.legacy.legacy.batteryChargeCounter);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
info.legacy.legacy.batteryCurrent);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
+ plugType(info.legacy.legacy));
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
+ info.legacy.legacy.batteryStatus);
synchronized (mLock) {
if (!mUpdatesStopped) {
@@ -471,6 +475,18 @@ public final class BatteryService extends SystemService {
dst.batteryTechnology = src.batteryTechnology;
}
+ private static int plugType(HealthInfo healthInfo) {
+ if (healthInfo.chargerAcOnline) {
+ return BatteryManager.BATTERY_PLUGGED_AC;
+ } else if (healthInfo.chargerUsbOnline) {
+ return BatteryManager.BATTERY_PLUGGED_USB;
+ } else if (healthInfo.chargerWirelessOnline) {
+ return BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ } else {
+ return BATTERY_PLUGGED_NONE;
+ }
+ }
+
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
@@ -478,15 +494,7 @@ public final class BatteryService extends SystemService {
mBatteryLevelCritical =
mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
- if (mHealthInfo.chargerAcOnline) {
- mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
- } else if (mHealthInfo.chargerUsbOnline) {
- mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
- } else if (mHealthInfo.chargerWirelessOnline) {
- mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
- } else {
- mPlugType = BATTERY_PLUGGED_NONE;
- }
+ mPlugType = plugType(mHealthInfo);
if (DEBUG) {
Slog.d(TAG, "Processing new values: "
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 66ac889ff2ca..fdda239e7fde 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -131,6 +131,7 @@ public class BinderCallsStatsService extends Binder {
private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
+ private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
private boolean mEnabled;
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -184,6 +185,9 @@ public class BinderCallsStatsService extends Binder {
mBinderCallsStats.setTrackDirectCallerUid(
mParser.getBoolean(SETTINGS_TRACK_DIRECT_CALLING_UID_KEY,
BinderCallsStats.DEFAULT_TRACK_DIRECT_CALLING_UID));
+ mBinderCallsStats.setIgnoreBatteryStatus(
+ mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+ BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS));
final boolean enabled =
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f6a29aa917f2..0a684287849a 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -63,8 +63,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
@@ -77,6 +75,8 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserRestrictionsUtils;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d586f0017a1e..1eba37d8b02d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -137,7 +137,6 @@ import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
-import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -190,6 +189,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.BasicShellCommandHandler;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 965b64bd9853..de40b5207a6e 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -52,6 +52,7 @@ public class LooperStatsService extends Binder {
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
+ private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
private static final String DEBUG_SYS_LOOPER_STATS_ENABLED =
"debug.sys.looper_stats_enabled";
private static final int DEFAULT_SAMPLING_INTERVAL = 1000;
@@ -64,6 +65,7 @@ public class LooperStatsService extends Binder {
// Default should be false so that the first call to #setEnabled installed the looper observer.
private boolean mEnabled = false;
private boolean mTrackScreenInteractive = false;
+ private boolean mIgnoreBatteryStatus = LooperStats.DEFAULT_IGNORE_BATTERY_STATUS;
private LooperStatsService(Context context, LooperStats stats) {
this.mContext = context;
@@ -85,6 +87,9 @@ public class LooperStatsService extends Binder {
setTrackScreenInteractive(
parser.getBoolean(SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY,
DEFAULT_TRACK_SCREEN_INTERACTIVE));
+ setIgnoreBatteryStatus(
+ parser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+ LooperStats.DEFAULT_IGNORE_BATTERY_STATUS));
// Manually specified value takes precedence over Settings.
setEnabled(SystemProperties.getBoolean(
DEBUG_SYS_LOOPER_STATS_ENABLED,
@@ -168,6 +173,14 @@ public class LooperStatsService extends Binder {
}
}
+ private void setIgnoreBatteryStatus(boolean ignore) {
+ if (mIgnoreBatteryStatus != ignore) {
+ mStats.setIgnoreBatteryStatus(ignore);
+ mIgnoreBatteryStatus = ignore;
+ mStats.reset();
+ }
+ }
+
private void setSamplingInterval(int samplingInterval) {
if (samplingInterval > 0) {
mStats.setSamplingInterval(samplingInterval);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 783866ab3420..8033ce787b5f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -102,7 +102,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.DiskInfo;
import android.os.storage.IObbActionListener;
import android.os.storage.IStorageEventListener;
@@ -151,6 +150,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.Installer;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 71a18218110e..5556e48a2018 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -24,7 +24,6 @@ import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
@@ -35,6 +34,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService.TargetUser;
import com.android.server.am.EventLogTags;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import dalvik.system.PathClassLoader;
diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java
index 76918529d071..d7e95df332fa 100644
--- a/services/core/java/com/android/server/adb/AdbShellCommand.java
+++ b/services/core/java/com/android/server/adb/AdbShellCommand.java
@@ -16,7 +16,7 @@
package com.android.server.adb;
-import android.os.BasicShellCommandHandler;
+import com.android.modules.utils.BasicShellCommandHandler;
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 01e77f8afdf3..d6f72990e4ac 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1630,6 +1630,8 @@ public final class ActiveServices {
}
}
+ // TODO: remove as part of fixing b/173627642
+ @SuppressWarnings("AndroidFrameworkCompatChange")
private void postFgsNotificationLocked(ServiceRecord r) {
boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled;
if (!showNow) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6765132ffd81..c3ba15024a78 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -37,7 +37,6 @@ import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
@@ -66,6 +65,7 @@ import com.android.internal.util.ParseUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
+import com.android.server.pm.UserManagerInternal;
import java.io.File;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 057b007cfb5e..364ad21b93cf 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -806,6 +806,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
void updateKeepWarmLocked() {
mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
&& (ams.mUserController.getCurrentUserId() == userId
+ || ams.mUserController.isCurrentProfile(userId)
|| ams.isSingleton(processName, appInfo, instanceName.getClassName(),
serviceInfo.flags));
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4c5b747cd167..12fcc9cc26bf 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -80,7 +80,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.format.DateUtils;
@@ -103,6 +102,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.am.UserState.KeyEvictedCallback;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 52fc93f101ab..89150aee44ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -121,8 +121,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
@@ -151,6 +149,8 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.audio.AudioServiceEvents.PhoneStateEvent;
import com.android.server.audio.AudioServiceEvents.VolumeEvent;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 2784f46a96c2..b430521a2885 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -543,6 +543,10 @@ public class BiometricScheduler {
return mCurrentOperation.clientMonitor;
}
+ public int getCurrentPendingCount() {
+ return mPendingOperations.size();
+ }
+
public void recordCrashState() {
if (mCrashStates.size() >= CrashState.NUM_ENTRIES) {
mCrashStates.removeFirst();
@@ -564,8 +568,22 @@ public class BiometricScheduler {
public void dump(PrintWriter pw) {
pw.println("Dump of BiometricScheduler " + getTag());
+ pw.println("Current operation: " + mCurrentOperation);
+ pw.println("Pending operations: " + mPendingOperations.size());
+ for (Operation operation : mPendingOperations) {
+ pw.println("Pending operation: " + operation);
+ }
for (CrashState crashState : mCrashStates) {
pw.println("Crash State " + crashState);
}
}
+
+ /**
+ * Clears the scheduler of anything work-related. This should be used for example when the
+ * HAL dies.
+ */
+ public void reset() {
+ mPendingOperations.clear();
+ mCurrentOperation = null;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 0dee81681fc4..bbd652357888 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -219,7 +219,7 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde
return mSensorId;
}
- public final T getFreshDaemon() {
+ public T getFreshDaemon() {
return mLazyDaemon.getDaemon();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 4906aacd06c0..47897a1a0588 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -64,6 +64,7 @@ import com.android.server.biometrics.sensors.face.hidl.Face10;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -425,6 +426,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
provider.dumpProtoMetrics(props.sensorId, fd);
}
}
+ } else if (args.length > 1 && "--hal".equals(args[0])) {
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
+ provider.dumpHal(props.sensorId, fd,
+ Arrays.copyOfRange(args, 1, args.length, args.getClass()));
+ }
+ }
} else {
for (ServiceProvider provider : mServiceProviders) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index d1578fa7bbf3..be4e2482acc9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -118,4 +118,6 @@ public interface ServiceProvider {
@NonNull
ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
+
+ void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
new file mode 100644
index 000000000000..13bbb7e8a704
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A test session implementation for {@link FaceProvider}. See
+ * {@link android.hardware.biometrics.BiometricTestSession}.
+ */
+public class BiometricTestSessionImpl extends ITestSession.Stub {
+
+ private static final String TAG = "BiometricTestSessionImpl";
+
+ @NonNull private final Context mContext;
+ private final int mSensorId;
+ @NonNull private final FaceProvider mProvider;
+ @NonNull private final Sensor mSensor;
+ @NonNull private final Set<Integer> mEnrollmentIds;
+ @NonNull private final Random mRandom;
+
+ /**
+ * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
+ * test, since enrollment is a platform-only API. The authentication path is tested through
+ * the public BiometricPrompt APIs and does not use this receiver.
+ */
+ private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() {
+ @Override
+ public void onEnrollResult(Face face, int remaining) {
+
+ }
+
+ @Override
+ public void onAcquired(int acquireInfo, int vendorCode) {
+
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+
+ }
+
+ @Override
+ public void onError(int error, int vendorCode) {
+
+ }
+
+ @Override
+ public void onRemoved(Face face, int remaining) {
+
+ }
+
+ @Override
+ public void onFeatureSet(boolean success, int feature) {
+
+ }
+
+ @Override
+ public void onFeatureGet(boolean success, int feature, boolean value) {
+
+ }
+
+ @Override
+ public void onChallengeGenerated(int sensorId, long challenge) {
+
+ }
+
+ @Override
+ public void onChallengeInterrupted(int sensorId) {
+
+ }
+
+ @Override
+ public void onChallengeInterruptFinished(int sensorId) {
+
+ }
+ };
+
+ BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull FaceProvider provider, @NonNull Sensor sensor) {
+ mContext = context;
+ mSensorId = sensorId;
+ mProvider = provider;
+ mSensor = sensor;
+ mEnrollmentIds = new HashSet<>();
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setTestHalEnabled(boolean enabled) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.setTestHalEnabled(enabled);
+ mSensor.setTestHalEnabled(enabled);
+ }
+
+ @Override
+ public void startEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
+ mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */);
+ }
+
+ @Override
+ public void finishEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ int nextRandomId = mRandom.nextInt();
+ while (mEnrollmentIds.contains(nextRandomId)) {
+ nextRandomId = mRandom.nextInt();
+ }
+
+ mEnrollmentIds.add(nextRandomId);
+ mSensor.getSessionForUser(userId).mHalSessionCallback
+ .onEnrollmentProgress(nextRandomId, 0 /* remaining */);
+ }
+
+ @Override
+ public void acceptAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ // Fake authentication with any of the existing faces
+ List<Face> faces = FaceUtils.getInstance(mSensorId)
+ .getBiometricsForUser(mContext, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "No faces, returning");
+ return;
+ }
+ final int fid = faces.get(0).getBiometricId();
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid,
+ HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69]));
+ }
+
+ @Override
+ public void rejectAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
+ }
+
+ @Override
+ public void notifyAcquired(int userId, int acquireInfo) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback
+ .onAcquired((byte) acquireInfo, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void notifyError(int userId, int errorCode) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode,
+ 0 /* vendorCode */);
+ }
+
+ @Override
+ public void cleanupInternalState(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.scheduleInternalCleanup(mSensorId, userId);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 3280e9815660..c27b6e5a4b7d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -47,6 +47,11 @@ class FaceGetAuthenticatorIdClient extends ClientMonitor<ISession> {
// Nothing to do here
}
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
@Override
protected void startHalOperation() {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 01c16fd1f4a6..26f62644e38b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -41,6 +41,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitor;
@@ -67,9 +68,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "FaceProvider";
private static final int ENROLL_TIMEOUT_SEC = 75;
+ private boolean mTestHalEnabled;
+
@NonNull private final Context mContext;
@NonNull private final String mHalInstanceName;
- @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+ @NonNull @VisibleForTesting
+ final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@NonNull private final ClientMonitor.LazyDaemon<IFace> mLazyDaemon;
@NonNull private final Handler mHandler;
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -150,6 +154,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Nullable
private synchronized IFace getHalInstance() {
+ if (mTestHalEnabled) {
+ return new TestHal();
+ }
+
if (mDaemon != null) {
return mDaemon;
}
@@ -525,7 +533,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
-
+ if (mSensors.contains(sensorId)) {
+ mSensors.get(sensorId).dumpProtoState(sensorId, proto);
+ }
}
@Override
@@ -576,18 +586,28 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull
@Override
public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
- return null; // TODO
+ return mSensors.get(sensorId).createTestSession();
}
@Override
+ public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {}
+
+ @Override
public void binderDied() {
Slog.e(getTag(), "HAL died");
mHandler.post(() -> {
mDaemon = null;
for (int i = 0; i < mSensors.size(); i++) {
+ final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+ sensor.getScheduler().recordCrashState();
+ sensor.getScheduler().reset();
}
});
}
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 5d100ec5e6dd..5b1f5465ea0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -61,6 +61,11 @@ public class FaceResetLockoutClient extends ClientMonitor<ISession> {
// Nothing to do here
}
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
@Override
protected void startHalOperation() {
try {
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 2dd6e7375613..d9d473722fd5 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
@@ -19,7 +19,9 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
@@ -31,10 +33,15 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+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;
@@ -56,6 +63,8 @@ import java.util.Map;
*/
public class Sensor implements IBinder.DeathRecipient {
+ private boolean mTestHalEnabled;
+
@NonNull private final String mTag;
@NonNull private final FaceProvider mProvider;
@NonNull private final Context mContext;
@@ -67,27 +76,6 @@ public class Sensor implements IBinder.DeathRecipient {
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
@Nullable private Session mCurrentSession;
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- mScheduler.recordCrashState();
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
- }
-
static class Session {
@NonNull final HalSessionCallback mHalSessionCallback;
@NonNull private final String mTag;
@@ -104,67 +92,6 @@ public class Sensor implements IBinder.DeathRecipient {
}
}
- Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
- @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
- mTag = tag;
- mProvider = provider;
- mContext = context;
- mHandler = handler;
- mSensorProperties = sensorProperties;
- mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
- mLockoutCache = new LockoutCache();
- mAuthenticatorIds = new HashMap<>();
- mLazySession = () -> (mCurrentSession != null) ? mCurrentSession.mSession : null;
- }
-
- @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
- return mLazySession;
- }
-
- @NonNull FaceSensorPropertiesInternal getSensorProperties() {
- return mSensorProperties;
- }
-
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- boolean hasSessionForUser(int userId) {
- return mCurrentSession != null && mCurrentSession.mUserId == userId;
- }
-
- @Nullable Session getSessionForUser(int userId) {
- if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
- return mCurrentSession;
- } else {
- return null;
- }
- }
-
- void createNewSession(@NonNull IFace daemon, int sensorId, int userId)
- throws RemoteException {
-
- final HalSessionCallback.Callback callback = () -> {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- };
- final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
- mTag, mScheduler, sensorId, userId, callback);
-
- final ISession newSession = daemon.createSession(sensorId, userId, resultController);
- newSession.asBinder().linkToDeath(this, 0 /* flags */);
- mCurrentSession = new Session(mTag, newSession, userId, resultController);
- }
-
- @NonNull BiometricScheduler getScheduler() {
- return mScheduler;
- }
-
- @NonNull LockoutCache getLockoutCache() {
- return mLockoutCache;
- }
-
- @NonNull Map<Integer, Long> getAuthenticatorIds() {
- return mAuthenticatorIds;
- }
-
static class HalSessionCallback extends ISessionCallback.Stub {
/**
* Interface to sends results to the HalSessionCallback's owner.
@@ -453,4 +380,120 @@ public class Sensor implements IBinder.DeathRecipient {
}
}
+
+ Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) {
+ mTag = tag;
+ mProvider = provider;
+ mContext = context;
+ mHandler = handler;
+ mSensorProperties = sensorProperties;
+ mScheduler = new BiometricScheduler(tag, null /* gestureAvailabilityDispatcher */);
+ mLockoutCache = new LockoutCache();
+ mAuthenticatorIds = new HashMap<>();
+ mLazySession = () -> {
+ if (mTestHalEnabled) {
+ return new TestSession(mCurrentSession.mHalSessionCallback);
+ } else {
+ return mCurrentSession != null ? mCurrentSession.mSession : null;
+ }
+ };
+ }
+
+ @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+ return mLazySession;
+ }
+
+ @NonNull FaceSensorPropertiesInternal getSensorProperties() {
+ return mSensorProperties;
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ boolean hasSessionForUser(int userId) {
+ return mCurrentSession != null && mCurrentSession.mUserId == userId;
+ }
+
+ @Nullable Session getSessionForUser(int userId) {
+ if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
+ return mCurrentSession;
+ } else {
+ return null;
+ }
+ }
+
+ @NonNull ITestSession createTestSession() {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
+ }
+
+ void createNewSession(@NonNull IFace daemon, int sensorId, int userId)
+ throws RemoteException {
+
+ final HalSessionCallback.Callback callback = () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ };
+ final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
+ mTag, mScheduler, sensorId, userId, callback);
+
+ final ISession newSession = daemon.createSession(sensorId, userId, resultController);
+ newSession.asBinder().linkToDeath(this, 0 /* flags */);
+ mCurrentSession = new Session(mTag, newSession, userId, resultController);
+ }
+
+ @NonNull BiometricScheduler getScheduler() {
+ return mScheduler;
+ }
+
+ @NonNull LockoutCache getLockoutCache() {
+ return mLockoutCache;
+ }
+
+ @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ return mAuthenticatorIds;
+ }
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
+
+ void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+ final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
+
+ proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(SensorStateProto.USER_STATES);
+ proto.write(UserStateProto.USER_ID, userId);
+ proto.write(UserStateProto.NUM_ENROLLED,
+ FaceUtils.getInstance(mSensorProperties.sensorId)
+ .getBiometricsForUser(mContext, userId).size());
+ proto.end(userToken);
+ }
+
+ proto.end(sensorToken);
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.e(mTag, "Binder died");
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FACE,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ mCurrentSession = null;
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
new file mode 100644
index 000000000000..34bf9bc63a64
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.ISessionCallback;
+import android.hardware.biometrics.face.SensorProps;
+import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Test HAL that provides only no-ops.
+ */
+public class TestHal extends IFace.Stub {
+ @Override
+ public SensorProps[] getSensorProps() {
+ return new SensorProps[0];
+ }
+
+ @Override
+ public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
+ return new ISession() {
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) {
+
+ }
+
+ @Override
+ public void revokeChallenge(int cookie, long challenge) {
+
+ }
+
+ @Override
+ public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+ NativeHandle previewSurface) {
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal authenticate(int cookie, long operationId) {
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction(int cookie) {
+ return null;
+ }
+
+ @Override
+ public void enumerateEnrollments(int cookie) {
+
+ }
+
+ @Override
+ public void removeEnrollments(int cookie, int[] enrollmentIds) {
+
+ }
+
+ @Override
+ public void getAuthenticatorId(int cookie) {
+
+ }
+
+ @Override
+ public void invalidateAuthenticatorId(int cookie) {
+
+ }
+
+ @Override
+ public void resetLockout(int cookie, HardwareAuthToken hat) {
+
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return new Binder();
+ }
+ };
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
new file mode 100644
index 000000000000..9707eddfee74
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.common.NativeHandle;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * Test session that provides mostly no-ops.
+ */
+public class TestSession extends ISession.Stub {
+ private static final String TAG = "TestSession";
+
+ @NonNull
+ private final Sensor.HalSessionCallback mHalSessionCallback;
+
+ TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
+ mHalSessionCallback = halSessionCallback;
+ }
+
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) {
+ mHalSessionCallback.onChallengeGenerated(0 /* challenge */);
+ }
+
+ @Override
+ public void revokeChallenge(int cookie, long challenge) {
+ mHalSessionCallback.onChallengeRevoked(challenge);
+ }
+
+ @Override
+ public ICancellationSignal enroll(int cookie, HardwareAuthToken hat,
+ NativeHandle previewSurface) {
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal authenticate(int cookie, long operationId) {
+ return new ICancellationSignal() {
+ @Override
+ public void cancel() throws RemoteException {
+ mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return new Binder();
+ }
+ };
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction(int cookie) {
+ return null;
+ }
+
+ @Override
+ public void enumerateEnrollments(int cookie) {
+
+ }
+
+ @Override
+ public void removeEnrollments(int cookie, int[] enrollmentIds) {
+
+ }
+
+ @Override
+ public void getAuthenticatorId(int cookie) {
+ mHalSessionCallback.onAuthenticatorIdRetrieved(0);
+ }
+
+ @Override
+ public void invalidateAuthenticatorId(int cookie) {
+
+ }
+
+ @Override
+ public void resetLockout(int cookie, HardwareAuthToken hat) {
+ mHalSessionCallback.onLockoutCleared();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 27ca33de415d..3555bbe629ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -333,16 +333,17 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@VisibleForTesting
- public Face10(@NonNull Context context, int sensorId,
+ Face10(@NonNull Context context, int sensorId,
@BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
- boolean supportsSelfIllumination, int maxTemplatesAllowed) {
+ boolean supportsSelfIllumination, int maxTemplatesAllowed,
+ @NonNull BiometricScheduler scheduler) {
mSensorProperties = new FaceSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength),
maxTemplatesAllowed, false /* supportsFaceDetect */, supportsSelfIllumination);
mContext = context;
mSensorId = sensorId;
- mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
+ mScheduler = scheduler;
mHandler = new Handler(Looper.getMainLooper());
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
@@ -369,7 +370,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
this(context, sensorId, strength, lockoutResetDispatcher,
context.getResources().getBoolean(R.bool.config_faceAuthSupportsSelfIllumination),
- context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser));
+ context.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser),
+ new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */));
}
@Override
@@ -388,12 +390,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mScheduler.recordCrashState();
-
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FACE,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
}
+
+ mScheduler.recordCrashState();
+ mScheduler.reset();
});
}
@@ -874,7 +877,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
});
}
- public void dumpHal(@NonNull FileDescriptor fd, @NonNull String[] args) {
+ /**
+ * Sends a debug message to the HAL with the provided FileDescriptor and arguments.
+ */
+ public void dumpHal(int sensorId, @NonNull FileDescriptor fd, @NonNull String[] args) {
// WARNING: CDD restricts image data from leaving TEE unencrypted on
// production devices:
// [C-1-10] MUST not allow unencrypted access to identifiable biometric
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 973132533661..3f9aef2b8651 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -143,7 +143,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
// Fake authentication with any of the existing fingers
- List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
+ List<Fingerprint> fingerprints = FingerprintUtils.getInstance(mSensorId)
.getBiometricsForUser(mContext, userId);
if (fingerprints.isEmpty()) {
Slog.w(TAG, "No fingerprints, returning");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 5f3be488c12c..db34d1444650 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -41,6 +41,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitor;
@@ -67,7 +68,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@NonNull private final Context mContext;
@NonNull private final String mHalInstanceName;
- @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+ @NonNull @VisibleForTesting
+ final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
@NonNull private final Handler mHandler;
@NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -607,8 +609,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mDaemon = null;
for (int i = 0; i < mSensors.size(); i++) {
+ final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+ sensor.getScheduler().recordCrashState();
+ sensor.getScheduler().reset();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 380608f1b0a9..ecb998594d44 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -80,27 +80,6 @@ class Sensor implements IBinder.DeathRecipient {
@Nullable private Session mCurrentSession;
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
-
- mScheduler.recordCrashState();
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
- }
-
static class Session {
@NonNull private final String mTag;
@NonNull private final ISession mSession;
@@ -501,11 +480,33 @@ class Sensor implements IBinder.DeathRecipient {
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
- .getBiometricsForUser(mContext, userId).size());
+ proto.write(UserStateProto.NUM_ENROLLED,
+ FingerprintUtils.getInstance(mSensorProperties.sensorId)
+ .getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
proto.end(sensorToken);
}
+
+ @Override
+ public void binderDied() {
+ Slog.e(mTag, "Binder died");
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ mCurrentSession = null;
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
index dd9150a32914..d6378780594f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
@@ -23,7 +23,7 @@ import android.hardware.keymaster.HardwareAuthToken;
import android.util.Slog;
/**
- * Test HAL that provides only provides mostly no-ops.
+ * Test session that provides mostly no-ops.
*/
class TestSession extends ISession.Stub {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 9924d4710436..11372a30599d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -386,12 +386,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mScheduler.recordCrashState();
-
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ISSUE_HAL_DEATH);
}
+
+ mScheduler.recordCrashState();
+ mScheduler.reset();
});
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4d959d08d3ef..bdd315de32b1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -207,7 +207,8 @@ public class Vpn {
@VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
- private String mInterface;
+ @VisibleForTesting
+ protected String mInterface;
private Connection mConnection;
/** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index d0cc8e71dbfa..42dcbeb73fff 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -501,15 +501,6 @@ final class Constants {
static final int DISABLED = 0;
static final int ENABLED = 1;
- @IntDef({
- VERSION_1_4,
- VERSION_2_0
- })
- @interface CecVersion {}
- static final int VERSION_1_3 = 0x04;
- static final int VERSION_1_4 = 0x05;
- static final int VERSION_2_0 = 0x06;
-
static final int ALL_DEVICE_TYPES_TV = 7;
static final int ALL_DEVICE_TYPES_RECORDER = 6;
static final int ALL_DEVICE_TYPES_TUNER = 5;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index e7f302c1977a..fb71d9510627 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
import android.annotation.CallSuper;
import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.input.InputManager;
@@ -560,7 +561,7 @@ abstract class HdmiCecLocalDevice {
protected abstract List<Integer> getDeviceFeatures();
protected boolean handleGiveFeatures(HdmiCecMessage message) {
- if (mService.getCecVersion() < Constants.VERSION_2_0) {
+ if (mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
return false;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 1a481b632606..96303ce9a76c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -16,10 +16,10 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import com.android.server.hdmi.Constants.AudioCodec;
-import com.android.server.hdmi.Constants.CecVersion;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
@@ -696,9 +696,9 @@ public class HdmiCecMessageBuilder {
return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES);
}
- static HdmiCecMessage buildReportFeatures(int src, @CecVersion int cecVersion,
- List<Integer> allDeviceTypes, int rcProfile, List<Integer> rcFeatures,
- List<Integer> deviceFeatures) {
+ static HdmiCecMessage buildReportFeatures(int src,
+ @HdmiControlManager.HdmiCecVersion int cecVersion, List<Integer> allDeviceTypes,
+ int rcProfile, List<Integer> rcFeatures, List<Integer> deviceFeatures) {
byte cecVersionByte = (byte) (cecVersion & 0xFF);
byte deviceTypes = 0;
for (Integer deviceType : allDeviceTypes) {
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 7fc3d2b302ff..c4c0f688bd67 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -31,7 +31,6 @@ import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -43,7 +42,7 @@ import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2d7f62d90914..a4844f6931ed 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -111,7 +111,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
@@ -184,6 +183,7 @@ import com.android.server.SystemService;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 77e2fbd25670..0e908d471f74 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -30,7 +30,6 @@ import android.os.Build;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -46,6 +45,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.textservices.TextServicesManagerInternal;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 8c1afab00b9a..7d3ccbf5a1b4 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -16,9 +16,10 @@
package com.android.server.location;
-import android.os.BasicShellCommandHandler;
import android.os.UserHandle;
+import com.android.modules.utils.BasicShellCommandHandler;
+
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ad96e7633283..f7f34407b867 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -83,7 +83,6 @@ import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
@@ -130,6 +129,7 @@ import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import libcore.util.HexEncoding;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
index 285e722886c2..9857fb637b59 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -16,24 +16,39 @@
package com.android.server.locksettings.recoverablekeystore;
+import android.security.keystore2.AndroidKeyStoreProvider;
+
import java.io.IOException;
-import java.security.cert.CertificateException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
/**
* Implementation of {@link KeyStoreProxy} that delegates all method calls to the {@link KeyStore}.
*/
public class KeyStoreProxyImpl implements KeyStoreProxy {
- private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore;
/**
+ * TODO This function redirects keystore access to the legacy keystore during a transitional
+ * phase during which not all calling code has been adjusted to use Keystore 2.0.
+ * This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete.
+ * The specific bug for this component is b/171305545.
+ */
+ static String androidKeystoreProviderName() {
+ if (AndroidKeyStoreProvider.isInstalled()) {
+ return "AndroidKeyStoreLegacy";
+ } else {
+ return "AndroidKeyStore";
+ }
+
+ }
+
+ /**
* A new instance, delegating to {@code keyStore}.
*/
public KeyStoreProxyImpl(KeyStore keyStore) {
@@ -69,7 +84,7 @@ public class KeyStoreProxyImpl implements KeyStoreProxy {
* @throws KeyStoreException if there was a problem getting or initializing the key store.
*/
public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
- KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
try {
keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index cb5ca85b0e18..dff1df7afa11 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -86,8 +86,6 @@ public class PlatformKeyManager {
private final KeyStoreProxy mKeyStore;
private final RecoverableKeyStoreDb mDatabase;
- private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
-
/**
* A new instance operating on behalf of {@code userId}, storing its prefs in the location
* defined by {@code context}.
@@ -472,7 +470,7 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was a problem getting or initializing the key store.
*/
private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
- KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName());
try {
keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 6e82c24a6e9b..f19d353a1f7b 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -28,14 +28,14 @@ import android.os.IBinder;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.service.oemlock.IOemLockService;
import android.util.Slog;
import com.android.server.LocalServices;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserRestrictionsUtils;
/**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index dd338655732b..c4a23f961072 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -73,7 +73,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
@@ -775,6 +774,9 @@ public class LauncherAppsService extends SystemService {
throw new IllegalArgumentException(
"To query by locus ID, package name must also be set");
}
+ if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+ ensureStrictAccessShortcutsPermission(callingPackage);
+ }
// TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
return new ParceledListSlice<>((List<ShortcutInfo>)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 4a799b5f86a9..4cee2e58d20e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -517,7 +517,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
String installerAttributionTag, int userId)
throws IOException {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(
+ mPm.enforceCrossUserPermission(
callingUid, userId, true, true, "createSession");
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -912,7 +912,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
@Override
public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(
+ mPm.enforceCrossUserPermission(
callingUid, userId, true, false, "getAllSessions");
final List<SessionInfo> result = new ArrayList<>();
@@ -930,7 +930,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
@Override
public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
- mPermissionManager.enforceCrossUserPermission(
+ mPm.enforceCrossUserPermission(
Binder.getCallingUid(), userId, true, false, "getMySessions");
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
@@ -954,7 +954,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+ mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
@@ -1006,7 +1006,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
String callerPackageName, IntentSender statusReceiver, int userId) {
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+ mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
@@ -1037,7 +1037,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
@Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
- mPermissionManager.enforceCrossUserPermission(
+ mPm.enforceCrossUserPermission(
Binder.getCallingUid(), userId, true, false, "registerCallback");
registerCallback(callback, eventUserId -> userId == eventUserId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a03c405c79f0..ad686f2cab89 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -160,6 +160,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
@@ -274,7 +275,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -4632,8 +4632,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) {
throw new SecurityException("User doesn't exist");
}
- mPermissionManager.enforceCrossUserPermission(
- callingUid, userId, false, false, "checkPackageStartable");
+ enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable");
final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -4664,8 +4663,8 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isPackageAvailable(String packageName, int userId) {
if (!mUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/, "is package available");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "is package available");
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
if (p != null) {
@@ -4707,7 +4706,7 @@ public class PackageManagerService extends IPackageManager.Stub
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId);
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
@@ -5009,8 +5008,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "getPackageUid");
return getPackageUidInternal(packageName, flags, userId, callingUid);
}
@@ -5042,8 +5041,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "getPackageGids");
// reader
synchronized (mLock) {
@@ -5126,7 +5125,7 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForApplication(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"get application info");
}
@@ -5430,8 +5429,7 @@ public class PackageManagerService extends IPackageManager.Stub
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
// require the permission to be held; the calling uid and given user id referring
// to the same user is not sufficient
- mPermissionManager.enforceCrossUserPermission(
- Binder.getCallingUid(), userId, false, false,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
+ Debug.getCallers(5));
@@ -5553,7 +5551,7 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForComponent(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
}
@@ -5635,8 +5633,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */, "get receiver info");
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get receiver info");
synchronized (mLock) {
ParsedActivity a = mComponentResolver.getReceiver(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -5744,9 +5742,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
"getDeclaredSharedLibraries");
int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getDeclaredSharedLibraries");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "getDeclaredSharedLibraries");
Preconditions.checkNotNull(packageName, "packageName cannot be null");
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
@@ -5860,9 +5857,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
- mPermissionManager.enforceCrossUserOrProfilePermission(
- callingUid, userId, false /* requireFullPermission */, false /* checkShell */,
- "get service info");
+ enforceCrossUserOrProfilePermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get service info");
synchronized (mLock) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -5891,8 +5887,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */, "get provider info");
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get provider info");
synchronized (mLock) {
ParsedProvider p = mComponentResolver.getProvider(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -6029,8 +6025,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) {
return null;
}
- mPermissionManager.enforceCrossUserPermission(
- callingUid, userId, false, false, "getChangedPackages");
+ enforceCrossUserPermission(callingUid, userId, false, false, "getChangedPackages");
synchronized (mLock) {
if (sequenceNumber >= mChangedPackagesSequenceNumber) {
return null;
@@ -6655,8 +6650,8 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
@@ -7363,7 +7358,7 @@ public class PackageManagerService extends IPackageManager.Stub
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
@@ -8170,9 +8165,8 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/,
- "query intent activity options");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "query intent activity options");
final String resultsAction = intent.getAction();
final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
@@ -8351,9 +8345,8 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/,
- "query intent receivers");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
@@ -8473,7 +8466,7 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
- mPermissionManager.enforceCrossUserOrProfilePermission(callingUid,
+ enforceCrossUserOrProfilePermission(callingUid,
userId,
false /*requireFullPermission*/,
false /*checkShell*/,
@@ -8757,9 +8750,8 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean listApex = (flags & MATCH_APEX) != 0;
final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */,
- "get installed packages");
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get installed packages");
// writer
synchronized (mLock) {
@@ -8869,9 +8861,8 @@ public class PackageManagerService extends IPackageManager.Stub
String[] permissions, int flags, int userId) {
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId);
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, false /* checkShell */,
- "get packages holding permissions");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ false /* checkShell */, "get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
@@ -8913,7 +8904,7 @@ public class PackageManagerService extends IPackageManager.Stub
flags = updateFlagsForApplication(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
- mPermissionManager.enforceCrossUserPermission(
+ enforceCrossUserPermission(
callingUid,
userId,
false /* requireFullPermission */,
@@ -8987,9 +8978,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getEphemeralApplications");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ false /* checkShell */, "getEphemeralApplications");
synchronized (mLock) {
List<InstantAppInfo> instantApps = mInstantAppRegistry
.getInstantAppsLPr(userId);
@@ -9003,9 +8993,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isInstantApp(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "isInstantApp");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "isInstantApp");
return isInstantAppInternal(packageName, userId, callingUid);
}
@@ -9039,9 +9028,8 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getInstantAppCookie");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ false /* checkShell */, "getInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return null;
}
@@ -9057,9 +9045,8 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, true /* checkShell */,
- "setInstantAppCookie");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ true /* checkShell */, "setInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return false;
}
@@ -9079,9 +9066,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getInstantAppIcon");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ false /* checkShell */, "getInstantAppIcon");
synchronized (mLock) {
return mInstantAppRegistry.getInstantAppIconLPw(
@@ -9157,8 +9143,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (!checkedGrants) {
- mPermissionManager.enforceCrossUserPermission(
- callingUid, userId, false, false, "resolveContentProvider");
+ enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider");
}
if (providerInfo == null) {
return null;
@@ -9843,6 +9828,171 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
+ message);
+ }
+
+ /**
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
+ * permission to be held even if the callingUid and userId
+ * reference the same user.
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell,
+ boolean requirePermissionWhenSameUser, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(
+ callingUid, callingUserId, userId, requireFullPermission,
+ requirePermissionWhenSameUser)) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserPermissionMessage(
+ callingUid, userId, message, requireFullPermission);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has the appropriate cross-user
+ * permissions defined as follows:
+ * <ul>
+ * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+ * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group
+ * to the caller.</li>
+ * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile
+ * group as the caller.</li>
+ * </ul>
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+ /*requirePermissionWhenSameUser= */ false)) {
+ return;
+ }
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+ mContext,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+ PermissionChecker.PID_UNKNOWN,
+ callingUid,
+ mPmInternal.getPackage(callingUid).getPackageName())
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+ callingUid, userId, message, requireFullPermission, isSameProfileGroup);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ private boolean hasCrossUserPermission(
+ int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+ boolean requirePermissionWhenSameUser) {
+ if (!requirePermissionWhenSameUser && userId == callingUserId) {
+ return true;
+ }
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
+ return true;
+ }
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static String buildInvalidCrossUserPermissionMessage(int callingUid,
+ @UserIdInt int userId, String message, boolean requireFullPermission) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("UID ");
+ builder.append(callingUid);
+ builder.append(" requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (!requireFullPermission) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+ builder.append(" to access user ");
+ builder.append(userId);
+ builder.append(".");
+ return builder.toString();
+ }
+
+ private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
+ @UserIdInt int userId, String message, boolean requireFullPermission,
+ boolean isSameProfileGroup) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("UID ");
+ builder.append(callingUid);
+ builder.append(" requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (!requireFullPermission) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+ }
+ }
+ builder.append(" to access user ");
+ builder.append(".");
+ return builder.toString();
+ }
+
@Override
public void performFstrimIfNeeded() {
enforceSystemOrRoot("Only the system can request fstrim");
@@ -13271,9 +13421,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "setApplicationHiddenSetting for user " + userId);
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ true /* checkShell */, "setApplicationHiddenSetting for user " + userId);
if (hidden && isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
@@ -13459,9 +13608,8 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getApplicationHidden for user " + userId);
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "getApplicationHidden for user " + userId);
PackageSetting ps;
final long callingId = Binder.clearCallingIdentity();
try {
@@ -13511,9 +13659,8 @@ public class PackageManagerService extends IPackageManager.Stub
+ android.Manifest.permission.INSTALL_PACKAGES + ".");
}
PackageSetting pkgSetting;
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */,
- "installExistingPackage for user " + userId);
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ true /* checkShell */, "installExistingPackage for user " + userId);
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
}
@@ -13878,9 +14025,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "isPackageSuspendedForUser for user " + userId);
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -17718,6 +17864,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ /*
+ * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+ * as this only works for packages that are installed
+ *
+ * TODO: Move logic for permission group compatibility into PermissionManagerService
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
+ return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+ }
+
@GuardedBy("mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
@@ -17760,7 +17917,7 @@ public class PackageManagerService extends IPackageManager.Stub
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- ParsedPackage parsedPackage;
+ final ParsedPackage parsedPackage;
try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
mPackageParserCallback)) {
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
@@ -17876,15 +18033,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /*
- * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
- * as this only works for packages that are installed
- *
- * TODO: Move logic for permission group compatibility into PermissionManagerService
- */
- boolean cannotInstallWithBadPermissionGroups =
- parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
-
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -17942,7 +18090,8 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.getPermissionGroups().get(groupNum);
final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
- if (sourceGroup != null && cannotInstallWithBadPermissionGroups) {
+ if (sourceGroup != null
+ && cannotInstallWithBadPermissionGroups(parsedPackage)) {
final String sourcePackageName = sourceGroup.packageName;
if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
@@ -18017,7 +18166,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) {
+ if (perm.getGroup() != null
+ && cannotInstallWithBadPermissionGroups(parsedPackage)) {
boolean isPermGroupDefinedByPackage = false;
for (int groupNum = 0; groupNum < numGroups; groupNum++) {
if (parsedPackage.getPermissionGroups().get(groupNum).getName()
@@ -19987,8 +20137,8 @@ public class PackageManagerService extends IPackageManager.Stub
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */, "clear application data");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "clear application data");
final boolean filterApp;
synchronized (mLock) {
@@ -20141,9 +20291,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
}
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- /* requireFullPermission= */ true, /* checkShell= */ false,
- "delete application cache files");
+ enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true,
+ /* checkShell= */ false, "delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
@@ -20269,8 +20418,8 @@ public class PackageManagerService extends IPackageManager.Stub
String opname, boolean removeExisting) {
// writer
int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "add preferred activity");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
@@ -20344,9 +20493,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "replace preferred activity");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "replace preferred activity");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
@@ -21555,8 +21703,8 @@ public class PackageManagerService extends IPackageManager.Stub
permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
}
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, true /* checkShell */, "set enabled");
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ true /* checkShell */, "set enabled");
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
@@ -21781,7 +21929,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) {
return;
}
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mLock) {
flushPackageRestrictionsAsUserInternalLocked(userId);
@@ -21846,8 +21994,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, true /* checkShell */, "stop package");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ true /* checkShell */, "stop package");
// writer
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -21994,8 +22142,8 @@ public class PackageManagerService extends IPackageManager.Stub
public int getApplicationEnabledSetting(String packageName, int userId) {
if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */, "get enabled");
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get enabled");
// reader
synchronized (mLock) {
try {
@@ -22015,8 +22163,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ false /*checkShell*/, "getComponentEnabled");
synchronized (mLock) {
try {
if (shouldFilterApplicationLocked(
@@ -26111,9 +26259,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getInstallReason(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /* requireFullPermission */, false /* checkShell */,
- "get install reason");
+ enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
+ false /* checkShell */, "get install reason");
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -26195,9 +26342,8 @@ public class PackageManagerService extends IPackageManager.Stub
public String getInstantAppAndroidId(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppAndroidId");
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
- true /* requireFullPermission */, false /* checkShell */,
- "getInstantAppAndroidId");
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
+ false /* checkShell */, "getInstantAppAndroidId");
// Make sure the target is an Instant App.
if (!isInstantApp(packageName, userId)) {
return null;
@@ -26313,8 +26459,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo");
+ enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
+ true /*checkShell*/, "setHarmfulAppInfo");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
@@ -26334,8 +26480,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo");
+ enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
+ true /*checkShell*/, "getHarmfulAppInfo");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
@@ -26353,8 +26499,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final int callingAppId = UserHandle.getAppId(callingUid);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, true /*checkShell*/, "isPackageStateProtected");
+ enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ true /*checkShell*/, "isPackageStateProtected");
if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID
&& checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid) != PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b05fd47383fd..9f1d6566e279 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -46,7 +46,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.incremental.IncrementalManager;
import android.os.incremental.V4Signature;
import android.os.incremental.V4Signature.HashingInfo;
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 69721d0769cb..804faa1b7f0f 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -252,43 +252,6 @@ public class PreferredComponent {
return numMatch == NS;
}
- public boolean sameSet(PreferredComponent pc) {
- if (mSetPackages == null || pc == null || pc.mSetPackages == null
- || !sameComponent(pc.mComponent)) {
- return false;
- }
- final int otherPackageCount = pc.mSetPackages.length;
- final int packageCount = mSetPackages.length;
- int numMatch = 0;
- for (int i = 0; i < otherPackageCount; i++) {
- boolean good = false;
- for (int j = 0; j < packageCount; j++) {
- if (mSetPackages[j].equals(pc.mSetPackages[j])
- && mSetClasses[j].equals(pc.mSetClasses[j])) {
- numMatch++;
- good = true;
- break;
- }
- }
- if (!good) {
- return false;
- }
- }
- return numMatch == packageCount;
- }
-
- /** Returns true if the preferred component represents the provided ComponentName. */
- private boolean sameComponent(ComponentName comp) {
- if (mComponent == null || comp == null) {
- return false;
- }
- if (mComponent.getPackageName().equals(comp.getPackageName())
- && mComponent.getClassName().equals(comp.getClassName())) {
- return true;
- }
- return false;
- }
-
public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) {
if (mSetPackages == null) {
return query == null;
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index ff3df130a3cc..a261e29b05a7 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -22,7 +22,6 @@ import android.content.IntentFilter;
import java.io.PrintWriter;
import com.android.server.IntentResolver;
-import java.util.ArrayList;
public class PreferredIntentResolver
extends IntentResolver<PreferredActivity, PreferredActivity> {
@@ -46,24 +45,4 @@ public class PreferredIntentResolver
protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) {
return input;
}
-
- public boolean shouldAddPreferredActivity(PreferredActivity pa) {
- ArrayList<PreferredActivity> pal = findFilters(pa);
- if (pal == null || pal.isEmpty()) {
- return true;
- }
- if (!pa.mPref.mAlways) {
- return false;
- }
- final int activityCount = pal.size();
- for (int i = 0; i < activityCount; i++) {
- PreferredActivity cur = pal.get(i);
- if (cur.mPref.mAlways
- && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK)
- && cur.mPref.sameSet(pa.mPref)) {
- return false;
- }
- }
- return true;
- }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9a5e05f38819..966090cb96a7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1312,7 +1312,8 @@ public final class Settings {
PreferredActivity pa = new PreferredActivity(parser);
if (pa.mPref.getParseError() == null) {
final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
- if (resolver.shouldAddPreferredActivity(pa)) {
+ ArrayList<PreferredActivity> pal = resolver.findFilters(pa);
+ if (pal == null || pal.size() == 0 || pa.mPref.mAlways) {
resolver.addFilter(pa);
}
} else {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d7c46a50e1c5..2d771820865d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -83,7 +83,6 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArraySet;
@@ -2838,10 +2837,14 @@ public class ShortcutService extends IShortcutService.Stub {
int queryFlags, int userId, int callingPid, int callingUid) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- final boolean cloneKeyFieldOnly =
- ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
- final int cloneFlag = cloneKeyFieldOnly ? ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO
- : ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+ int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
+ if ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0) {
+ flags = ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+ } else if ((queryFlags & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
+ flags &= ~ShortcutInfo.CLONE_REMOVE_PERSON;
+ }
+ final int cloneFlag = flags;
+
if (packageName == null) {
shortcutIds = null; // LauncherAppsService already threw for it though.
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index d535f7b8e0f7..85c4ab2c8a10 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -49,7 +49,6 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.TextUtils;
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 76b5ba5eb82d..47d0a3d561b1 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.os;
+package com.android.server.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -22,8 +22,8 @@ import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
-
-import com.android.server.pm.RestrictionsSet;
+import android.os.Bundle;
+import android.os.UserManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 14352f3ddc49..c51e75c716cc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -78,8 +78,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
@@ -112,6 +110,7 @@ import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
import com.android.server.am.UserState;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 145dd24e2998..d0c3a95eafc7 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -33,7 +33,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.telephony.SubscriptionInfo;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f81906399be2..6c03a28d03c7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -77,7 +77,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -96,6 +95,7 @@ import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Build;
+import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -107,7 +107,6 @@ import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.permission.IOnPermissionsChangeListener;
import android.permission.IPermissionManager;
@@ -147,6 +146,7 @@ import com.android.server.pm.ApexManager;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -754,7 +754,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
enforceCrossUserPermission(callingUid, userId,
true, // requireFullPermission
false, // checkShell
- false, // requirePermissionWhenSameUser
"getPermissionFlags");
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -841,7 +840,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
enforceCrossUserPermission(callingUid, userId,
true, // requireFullPermission
true, // checkShell
- false, // requirePermissionWhenSameUser
"updatePermissionFlags");
if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) {
@@ -951,7 +949,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
enforceCrossUserPermission(callingUid, userId,
true, // requireFullPermission
true, // checkShell
- false, // requirePermissionWhenSameUser
"updatePermissionFlagsForAllApps");
// Only the system can change system fixed flags.
@@ -1555,7 +1552,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
enforceCrossUserPermission(callingUid, userId,
true, // requireFullPermission
true, // checkShell
- false, // requirePermissionWhenSameUser
"grantRuntimePermission");
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -1722,7 +1718,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
enforceCrossUserPermission(callingUid, userId,
true, // requireFullPermission
true, // checkShell
- false, // requirePermissionWhenSameUser
"revokeRuntimePermission");
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
@@ -4494,25 +4489,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
/**
- * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
- * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+ * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+ * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
+ *
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
- boolean requireFullPermission, boolean checkShell,
- boolean requirePermissionWhenSameUser, String message) {
+ boolean requireFullPermission, boolean checkShell, @Nullable String message) {
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
}
if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
}
final int callingUserId = UserHandle.getUserId(callingUid);
- if (hasCrossUserPermission(
- callingUid, callingUserId, userId, requireFullPermission,
- requirePermissionWhenSameUser)) {
+ if (checkCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission)) {
return;
}
String errorMessage = buildInvalidCrossUserPermissionMessage(
@@ -4522,82 +4514,45 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
/**
- * Checks if the request is from the system or an app that has the appropriate cross-user
- * permissions defined as follows:
- * <ul>
- * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
- * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group
- * to the caller.</li>
- * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group
- * as the caller.</li>
- * </ul>
- *
- * @param checkShell whether to prevent shell from access if there's a debugging restriction
- * @param message the message to log on security exception
+ * Enforces that if the caller is shell, it does not have the provided user restriction.
*/
- private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
- boolean requireFullPermission, boolean checkShell,
- String message) {
- if (userId < 0) {
- throw new IllegalArgumentException("Invalid userId " + userId);
- }
- if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
- /*requirePermissionWhenSameUser= */ false)) {
- return;
- }
- final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
- if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
- mContext,
- android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- PermissionChecker.PID_UNKNOWN,
- callingUid,
- mPackageManagerInt.getPackage(callingUid).getPackageName())
- == PermissionChecker.PERMISSION_GRANTED) {
- return;
+ private void enforceShellRestriction(@NonNull String restriction, int callingUid,
+ @UserIdInt int userId) {
+ if (callingUid == Process.SHELL_UID) {
+ if (userId >= 0 && mUserManagerInt.hasUserRestriction(restriction, userId)) {
+ throw new SecurityException("Shell does not have permission to access user "
+ + userId);
+ } else if (userId < 0) {
+ Slog.e(LOG_TAG, "Unable to check shell permission for user "
+ + userId + "\n\t" + Debug.getCallers(3));
+ }
}
- String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
- callingUid, userId, message, requireFullPermission, isSameProfileGroup);
- Slog.w(TAG, errorMessage);
- throw new SecurityException(errorMessage);
}
- private boolean hasCrossUserPermission(
- int callingUid, int callingUserId, int userId, boolean requireFullPermission,
- boolean requirePermissionWhenSameUser) {
- if (!requirePermissionWhenSameUser && userId == callingUserId) {
+ private boolean checkCrossUserPermission(int callingUid, @UserIdInt int callingUserId,
+ @UserIdInt int userId, boolean requireFullPermission) {
+ if (userId == callingUserId) {
return true;
}
if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
return true;
}
if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ return checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
}
- private boolean hasPermission(String permission) {
+ private boolean checkCallingOrSelfPermission(String permission) {
return mContext.checkCallingOrSelfPermission(permission)
== PackageManager.PERMISSION_GRANTED;
}
- private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
+ @NonNull
private static String buildInvalidCrossUserPermissionMessage(int callingUid,
- @UserIdInt int userId, String message, boolean requireFullPermission) {
+ @UserIdInt int userId, @Nullable String message, boolean requireFullPermission) {
StringBuilder builder = new StringBuilder();
if (message != null) {
builder.append(message);
@@ -4617,31 +4572,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return builder.toString();
}
- private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
- @UserIdInt int userId, String message, boolean requireFullPermission,
- boolean isSameProfileGroup) {
- StringBuilder builder = new StringBuilder();
- if (message != null) {
- builder.append(message);
- builder.append(": ");
- }
- builder.append("UID ");
- builder.append(callingUid);
- builder.append(" requires ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- if (!requireFullPermission) {
- builder.append(" or ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
- if (isSameProfileGroup) {
- builder.append(" or ");
- builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
- }
- }
- builder.append(" to access user ");
- builder.append(".");
- return builder.toString();
- }
-
@GuardedBy("mLock")
private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) {
int size = 0;
@@ -5140,30 +5070,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
Preconditions.checkArgumentNonNegative(userId, "userId");
mPackageManagerInt.forEachPackage(pkg -> resetRuntimePermissionsInternal(pkg, userId));
}
- @Override
- public void enforceCrossUserPermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell, String message) {
- PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
- requireFullPermission, checkShell, false, message);
- }
- @Override
- public void enforceCrossUserPermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell,
- boolean requirePermissionWhenSameUser, String message) {
- PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
- requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
- }
-
- @Override
- public void enforceCrossUserOrProfilePermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell, String message) {
- PermissionManagerService.this.enforceCrossUserOrProfilePermission(
- callingUid,
- userId,
- requireFullPermission,
- checkShell,
- message);
- }
@Override
public Permission getPermissionTEMP(String permName) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 7656b2ef7b04..df9d0d397c56 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -393,32 +393,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
@NonNull
public abstract String[] getAppOpPermissionPackages(@NonNull String permissionName);
- /**
- * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
- * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller.
- * @param checkShell whether to prevent shell from access if there's a debugging restriction
- * @param message the message to log on security exception
- */
- public abstract void enforceCrossUserPermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell, @NonNull String message);
-
- /**
- * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)}
- * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are
- * in the same profile group.
- */
- public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell, @NonNull String message);
-
- /**
- * @see #enforceCrossUserPermission(int, int, boolean, boolean, String)
- * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
- * permission to be held even if the callingUid and userId reference the same user.
- */
- public abstract void enforceCrossUserPermission(int callingUid, int userId,
- boolean requireFullPermission, boolean checkShell,
- boolean requirePermissionWhenSameUser, @NonNull String message);
-
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
@Nullable
public abstract Permission getPermissionTEMP(@NonNull String permName);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index e1cd9e334f4c..907743314c7c 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -47,7 +47,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
import android.provider.Settings;
import android.provider.Telephony;
@@ -68,7 +67,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f7e6822612d8..8beec35ebc64 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@ import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
@@ -1959,14 +1958,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(int transit, long duration,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
- return handleStartTransitionForKeyguardLw(transit, duration);
+ return handleStartTransitionForKeyguardLw(keyguardGoingAway, duration);
}
@Override
- public void onAppTransitionCancelledLocked(int transit) {
- handleStartTransitionForKeyguardLw(transit, 0 /* duration */);
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
}
});
mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
@@ -3194,7 +3193,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private int handleStartTransitionForKeyguardLw(int transit, long duration) {
+ private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
if (mKeyguardOccludedChanged) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
+ mPendingKeyguardOccluded);
@@ -3203,7 +3202,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
}
}
- if (AppTransition.isKeyguardGoingAwayTransit(transit)) {
+ if (keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index e7b1b4e4a687..ab6ada2f85f7 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -46,7 +46,6 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -71,7 +70,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.ByteArrayOutputStream;
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 1c29c69239f6..b79fc8dd1e1a 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -35,7 +35,6 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
import android.os.storage.StorageVolume;
import android.service.storage.ExternalStorageService;
@@ -46,6 +45,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import java.io.IOException;
import java.util.HashMap;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index afbe5527a5cb..510893b2940b 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -87,6 +87,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.SystemService;
@@ -337,6 +338,7 @@ public final class TvInputManagerService extends SystemService {
inputState = new TvInputState();
}
inputState.info = info;
+ inputState.uid = getInputUid(info);
inputMap.put(info.getId(), inputState);
}
@@ -371,6 +373,16 @@ public final class TvInputManagerService extends SystemService {
userState.inputMap = inputMap;
}
+ private int getInputUid(TvInputInfo info) {
+ try {
+ return getContext().getPackageManager().getApplicationInfo(
+ info.getServiceInfo().packageName, 0).uid;
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to get UID for " + info, e);
+ return Process.INVALID_UID;
+ }
+ }
+
@GuardedBy("mLock")
private void buildTvContentRatingSystemListLocked(int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
@@ -405,7 +417,7 @@ public final class TvInputManagerService extends SystemService {
return;
}
if (mUserStates.contains(mCurrentUserId)) {
- UserState userState = mUserStates.get(mCurrentUserId);
+ UserState userState = getUserStateLocked(mCurrentUserId);
List<SessionState> sessionStatesToRelease = new ArrayList<>();
for (SessionState sessionState : userState.sessionStateMap.values()) {
if (sessionState.session != null && !sessionState.isRecordingSession) {
@@ -474,7 +486,7 @@ public final class TvInputManagerService extends SystemService {
private void removeUser(int userId) {
synchronized (mLock) {
- UserState userState = mUserStates.get(userId);
+ UserState userState = getUserStateLocked(userId);
if (userState == null) {
return;
}
@@ -535,7 +547,7 @@ public final class TvInputManagerService extends SystemService {
@GuardedBy("mLock")
private UserState getOrCreateUserStateLocked(int userId) {
- UserState userState = mUserStates.get(userId);
+ UserState userState = getUserStateLocked(userId);
if (userState == null) {
userState = new UserState(mContext, userId);
mUserStates.put(userId, userState);
@@ -715,7 +727,8 @@ public final class TvInputManagerService extends SystemService {
}
@GuardedBy("mLock")
- private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ @Nullable
+ private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
SessionState sessionState = null;
try {
sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
@@ -738,6 +751,7 @@ public final class TvInputManagerService extends SystemService {
}
}
removeSessionStateLocked(sessionToken, userId);
+ return sessionState;
}
@GuardedBy("mLock")
@@ -908,6 +922,7 @@ public final class TvInputManagerService extends SystemService {
return;
}
inputState.info = inputInfo;
+ inputState.uid = getInputUid(inputInfo);
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
@@ -1248,7 +1263,22 @@ public final class TvInputManagerService extends SystemService {
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
userId, "createSession");
final long identity = Binder.clearCallingIdentity();
- // Generate a unique session id with a random UUID.
+ /**
+ * A randomly generated id for this this session.
+ *
+ * <p>This field contains no user or device reference and is large enough to be
+ * effectively globally unique.
+ *
+ * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+ * Inspect the code at:
+ *
+ * <ul>
+ * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+ * <li>{@link #logTuneStateChanged}
+ * <li>{@link TvInputManagerService.BinderService#createSession}
+ * <li>{@link SessionState#sessionId}
+ * </ul>
+ */
String uniqueSessionId = UUID.randomUUID().toString();
try {
synchronized (mLock) {
@@ -1269,6 +1299,8 @@ public final class TvInputManagerService extends SystemService {
TvInputInfo info = inputState.info;
ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
if (serviceState == null) {
+ int tisUid = PackageManager.getApplicationInfoAsUserCached(
+ info.getComponent().getPackageName(), 0, resolvedUserId).uid;
serviceState = new ServiceState(info.getComponent(), resolvedUserId);
userState.serviceStateMap.put(info.getComponent(), serviceState);
}
@@ -1301,6 +1333,8 @@ public final class TvInputManagerService extends SystemService {
} else {
updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
}
+ logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED,
+ sessionState, inputState);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1317,8 +1351,17 @@ public final class TvInputManagerService extends SystemService {
userId, "releaseSession");
final long identity = Binder.clearCallingIdentity();
try {
+ SessionState sessionState = null;
+ UserState userState = null;
synchronized (mLock) {
- releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+ sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
+ userState = getUserStateLocked(userId);
+ }
+ if (sessionState != null) {
+ TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState,
+ userState);
+ logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED,
+ sessionState, tvInputState);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1372,10 +1415,13 @@ public final class TvInputManagerService extends SystemService {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "setSurface");
final long identity = Binder.clearCallingIdentity();
+ SessionState sessionState = null;
+ UserState userState = null;
try {
synchronized (mLock) {
try {
- SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ userState = getUserStateLocked(userId);
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
resolvedUserId);
if (sessionState.hardwareSessionToken == null) {
getSessionLocked(sessionState).setSurface(surface);
@@ -1392,6 +1438,14 @@ public final class TvInputManagerService extends SystemService {
// surface is not used in TvInputManagerService.
surface.release();
}
+ if (sessionState != null) {
+ int state = surface == null
+ ?
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED
+ : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED;
+ logTuneStateChanged(state, sessionState,
+ TvInputManagerService.getTvInputState(sessionState, userState));
+ }
Binder.restoreCallingIdentity(identity);
}
}
@@ -1479,6 +1533,10 @@ public final class TvInputManagerService extends SystemService {
return;
}
+ logTuneStateChanged(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED,
+ sessionState,
+ TvInputManagerService.getTvInputState(sessionState, userState));
// Log the start of watch.
SomeArgs args = SomeArgs.obtain();
args.arg1 = sessionState.componentName.getPackageName();
@@ -2302,6 +2360,16 @@ public final class TvInputManagerService extends SystemService {
}
}
+ @Nullable
+ private static TvInputState getTvInputState(
+ SessionState sessionState,
+ @Nullable UserState userState) {
+ if (userState != null) {
+ return userState.inputMap.get(sessionState.inputId);
+ }
+ return null;
+ }
+
@GuardedBy("mLock")
private List<TunedInfo> getCurrentTunedInfosInternalLocked(
UserState userState, int callingPid, int callingUid) {
@@ -2367,6 +2435,28 @@ public final class TvInputManagerService extends SystemService {
}
}
+ /**
+ * Log Tune state changes to {@link FrameworkStatsLog}.
+ *
+ * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+ * Inspect the code at:
+ *
+ * <ul>
+ * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+ * <li>{@link #logTuneStateChanged}
+ * <li>{@link TvInputManagerService.BinderService#createSession}
+ * <li>{@link SessionState#sessionId}
+ * </ul>
+ */
+ private void logTuneStateChanged(int state, SessionState sessionState,
+ @Nullable TvInputState inputState) {
+ // TODO(b/173536904): log input type and id
+ FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED,
+ new int[]{sessionState.callingUid,
+ inputState == null ? Process.INVALID_UID : inputState.uid},
+ new String[]{"tif_player", "tv_input_service"}, state, sessionState.sessionId);
+ }
+
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -2464,10 +2554,25 @@ public final class TvInputManagerService extends SystemService {
}
private static final class TvInputState {
- // A TvInputInfo object which represents the TV input.
+
+ /** A TvInputInfo object which represents the TV input. */
private TvInputInfo info;
- // The state of TV input. Connected by default.
+ /**
+ * The kernel user-ID that has been assigned to the application the TvInput is a part of.
+ *
+ * <p>
+ * Currently this is not a unique ID (multiple applications can have
+ * the same uid).
+ */
+ private int uid;
+
+ /**
+ * The state of TV input.
+ *
+ * <p>
+ * Connected by default
+ */
private int state = INPUT_STATE_CONNECTED;
@Override
@@ -2478,6 +2583,23 @@ public final class TvInputManagerService extends SystemService {
private final class SessionState implements IBinder.DeathRecipient {
private final String inputId;
+
+ /**
+ * A randomly generated id for this this session.
+ *
+ * <p>This field contains no user or device reference and is large enough to be
+ * effectively globally unique.
+ *
+ * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy.
+ * Inspect the code at:
+ *
+ * <ul>
+ * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState
+ * <li>{@link #logTuneStateChanged}
+ * <li>{@link TvInputManagerService.BinderService#createSession}
+ * <li>{@link SessionState#sessionId}
+ * </ul>
+ */
private final String sessionId;
private final ComponentName componentName;
private final boolean isRecordingSession;
@@ -2545,7 +2667,7 @@ public final class TvInputManagerService extends SystemService {
Slog.d(TAG, "onServiceConnected(component=" + component + ")");
}
synchronized (mLock) {
- UserState userState = mUserStates.get(mUserId);
+ UserState userState = getUserStateLocked(mUserId);
if (userState == null) {
// The user was removed while connecting.
mContext.unbindService(this);
@@ -2815,8 +2937,13 @@ public final class TvInputManagerService extends SystemService {
if (mSessionState.session == null || mSessionState.client == null) {
return;
}
+ TvInputState tvInputState = getTvInputState(mSessionState,
+ getUserStateLocked(mCurrentUserId));
try {
mSessionState.client.onVideoAvailable(mSessionState.seq);
+ logTuneStateChanged(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE,
+ mSessionState, tvInputState);
} catch (RemoteException e) {
Slog.e(TAG, "error in onVideoAvailable", e);
}
@@ -2832,8 +2959,20 @@ public final class TvInputManagerService extends SystemService {
if (mSessionState.session == null || mSessionState.client == null) {
return;
}
+ TvInputState tvInputState = getTvInputState(mSessionState,
+ getUserStateLocked(mCurrentUserId));
try {
mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
+ int loggedReason = reason + FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+ if (loggedReason < FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN
+ || loggedReason > FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) {
+ loggedReason = FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+ }
+ logTuneStateChanged(loggedReason, mSessionState, tvInputState);
} catch (RemoteException e) {
Slog.e(TAG, "error in onVideoUnavailable", e);
}
@@ -3019,6 +3158,10 @@ public final class TvInputManagerService extends SystemService {
}
}
+ private UserState getUserStateLocked(int userId) {
+ return mUserStates.get(userId);
+ }
+
private static final class WatchLogHandler extends Handler {
// There are only two kinds of watch events that can happen on the system:
// 1. The current TV input session is tuned to a new channel.
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 329589eea904..e07540a0c746 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -78,7 +78,6 @@ import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine;
@@ -107,7 +106,7 @@ import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d8d1a6563675..b4ca7c5f6ff1 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,8 +3,10 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WaitResult.INVALID_DELAY;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH;
import static android.app.WaitResult.LAUNCH_STATE_WARM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -130,7 +132,6 @@ class ActivityMetricsLogger {
* transition, in the case the launch is standalone (e.g. from recents).
*/
private static final int IGNORE_CALLER = -1;
- private static final int INVALID_DELAY = -1;
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
@@ -220,6 +221,8 @@ class ActivityMetricsLogger {
boolean mLoggedStartingWindowDrawn;
/** If the any app transitions have been logged as starting. */
boolean mLoggedTransitionStarting;
+ /** Whether any activity belonging to this transition has relaunched. */
+ boolean mRelaunched;
/** Non-null if the application has reported drawn but its window hasn't. */
@Nullable Runnable mPendingFullyDrawn;
@@ -351,6 +354,7 @@ class ActivityMetricsLogger {
*/
final int windowsFullyDrawnDelayMs;
final int activityRecordIdHashCode;
+ final boolean relaunched;
private TransitionInfoSnapshot(TransitionInfo info) {
this(info, info.mLastLaunchedActivity, INVALID_DELAY);
@@ -379,6 +383,7 @@ class ActivityMetricsLogger {
launchedActivityShortComponentName = launchedActivity.shortComponentName;
activityRecordIdHashCode = System.identityHashCode(launchedActivity);
this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
+ relaunched = info.mRelaunched;
}
@WaitResult.LaunchState int getLaunchState() {
@@ -386,7 +391,7 @@ class ActivityMetricsLogger {
case TYPE_TRANSITION_WARM_LAUNCH:
return LAUNCH_STATE_WARM;
case TYPE_TRANSITION_HOT_LAUNCH:
- return LAUNCH_STATE_HOT;
+ return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT;
case TYPE_TRANSITION_COLD_LAUNCH:
return LAUNCH_STATE_COLD;
default:
@@ -673,6 +678,13 @@ class ActivityMetricsLogger {
}
}
+ void notifyActivityRelaunched(ActivityRecord r) {
+ final TransitionInfo info = getActiveTransitionInfo(r);
+ if (info != null) {
+ info.mRelaunched = true;
+ }
+ }
+
/** Makes sure that the reference to the removed activity is cleared. */
void notifyActivityRemoved(@NonNull ActivityRecord r) {
mLastTransitionInfo.remove(r);
@@ -800,13 +812,13 @@ class ActivityMetricsLogger {
FrameworkStatsLog.APP_START_CANCELED,
activity.info.applicationInfo.uid,
activity.packageName,
- convertAppStartTransitionType(type),
+ getAppStartTransitionType(type, info.mRelaunched),
activity.info.name);
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
activity.info.applicationInfo.uid,
activity.packageName,
- convertAppStartTransitionType(type),
+ getAppStartTransitionType(type, info.mRelaunched),
activity.info.name));
}
}
@@ -871,7 +883,7 @@ class ActivityMetricsLogger {
FrameworkStatsLog.APP_START_OCCURRED,
info.applicationInfo.uid,
info.packageName,
- convertAppStartTransitionType(info.type),
+ getAppStartTransitionType(info.type, info.relaunched),
info.launchedActivityName,
info.launchedActivityLaunchedFromPackage,
isInstantApp,
@@ -891,7 +903,7 @@ class ActivityMetricsLogger {
Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
info.applicationInfo.uid,
info.packageName,
- convertAppStartTransitionType(info.type),
+ getAppStartTransitionType(info.type, info.relaunched),
info.launchedActivityName,
info.launchedActivityLaunchedFromPackage));
}
@@ -918,7 +930,7 @@ class ActivityMetricsLogger {
Log.i(TAG, sb.toString());
}
- private int convertAppStartTransitionType(int tronType) {
+ private static int getAppStartTransitionType(int tronType, boolean relaunched) {
if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
}
@@ -926,17 +938,13 @@ class ActivityMetricsLogger {
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM;
}
if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
- return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
+ return relaunched
+ ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH
+ : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT;
}
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
}
- /** @return the last known window drawn delay of the given activity. */
- int getLastDrawnDelayMs(ActivityRecord r) {
- final TransitionInfo info = mLastTransitionInfo.get(r);
- return info != null ? info.mWindowsDrawnDelayMs : INVALID_DELAY;
- }
-
/** @see android.app.Activity#reportFullyDrawn */
TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r,
boolean restoredFromBundle) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d8b750ea7484..8cc02e58b30f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -103,9 +103,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TransitionOldType;
@@ -150,7 +150,6 @@ import static com.android.server.wm.ActivityRecordProto.VISIBLE;
import static com.android.server.wm.ActivityRecordProto.VISIBLE_REQUESTED;
import static com.android.server.wm.ActivityRecordProto.VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW;
import static com.android.server.wm.ActivityRecordProto.WINDOW_TOKEN;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -177,6 +176,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_F
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -226,7 +226,7 @@ import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
-import android.app.WaitResult.LaunchState;
+import android.app.WaitResult;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -2609,7 +2609,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- mDisplayContent.prepareAppTransitionOld(transit, false);
mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
// When finishing the activity preemptively take the snapshot before the app window
@@ -2693,7 +2692,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) {
final DisplayContent dc = mDisplayContent;
- dc.prepareAppTransitionOld(transit, false);
dc.prepareAppTransition(TRANSIT_CLOSE);
setVisibility(false);
dc.executeAppTransition();
@@ -3132,6 +3130,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void finishRelaunching() {
+ mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
unfreezeBounds();
if (mPendingRelaunchCount > 0) {
@@ -4274,14 +4273,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
displayContent.mClosingApps.add(this);
mEnteringAnimation = false;
}
- if (appTransition.getAppTransitionOld() == TRANSIT_OLD_TASK_OPEN_BEHIND) {
+ if ((appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win = getDisplayContent().findFocusedWindow();
if (win != null) {
final ActivityRecord focusedActivity = win.mActivityRecord;
if (focusedActivity != null) {
ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps",
+ "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
focusedActivity);
// Force animation to be loaded.
@@ -4297,7 +4296,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
- boolean applyAnimation(LayoutParams lp, int transit, boolean enter,
+ boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
if (mUseTransferredAnimation) {
return false;
@@ -5437,14 +5436,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
.getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
final boolean validInfo = info != null;
final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
- final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1;
+ final @WaitResult.LaunchState int launchState =
+ validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN;
// The activity may have been requested to be invisible (another activity has been launched)
// so there is no valid info. But if it is the current top activity (e.g. sleeping), the
// invalid state is still reported to make sure the waiting result is notified.
if (validInfo || this == getDisplayArea().topRunningActivity()) {
mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
windowsDrawnDelayMs, launchState);
- mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+ mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState);
}
finishLaunchTickingLocked();
if (task != null) {
@@ -6312,7 +6312,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mThumbnail = null;
}
- public int getTransit() {
+ public @TransitionOldType int getTransit() {
return mTransit;
}
@@ -7165,11 +7165,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
mRelaunchReason = RELAUNCH_REASON_NONE;
}
- if (!attachedToProcess()) {
- ProtoLog.v(WM_DEBUG_CONFIGURATION,
- "Config is destroying non-running %s", this);
- destroyImmediately("config");
- } else if (mState == PAUSING) {
+ if (mState == PAUSING) {
// A little annoying: we are waiting for this activity to finish pausing. Let's not
// do anything now, but just flag that it needs to be restarted when done pausing.
ProtoLog.v(WM_DEBUG_CONFIGURATION,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f472a5dd7ba9..d560839f465b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -28,8 +28,6 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WaitResult.LAUNCH_STATE_COLD;
-import static android.app.WaitResult.LAUNCH_STATE_HOT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -819,8 +817,6 @@ class ActivityStarter {
break;
}
case START_TASK_TO_FRONT: {
- mRequest.waitResult.launchState =
- r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD;
// ActivityRecord may represent a different activity, but it should not be
// in the resumed state.
if (r.nowVisible && r.isState(RESUMED)) {
@@ -1285,6 +1281,15 @@ class ActivityStarter {
return false;
}
+ // IME should always be allowed to start activity, like IME settings.
+ final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
+ if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
+ }
+ return false;
+ }
+
// App switching will be allowed if BAL app switching flag is not enabled, or if
// its app switching rule allows it.
// This is used to block background activity launch even if the app is still
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 43326bd2e8ce..e4c607dd8cdd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -67,7 +67,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -95,10 +94,6 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
@@ -117,6 +112,10 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEI
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -164,7 +163,6 @@ import android.app.assist.AssistStructure;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.app.usage.UsageStatsManagerInternal;
-import android.compat.annotation.ChangeId;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -6248,7 +6246,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final boolean wasTransitionSet = dc.mAppTransition.isTransitionSet();
if (!wasTransitionSet) {
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false /* alwaysKeepCurrent */);
dc.prepareAppTransition(TRANSIT_NONE);
}
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e91a6d8e2439..01d77d5b6cc8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -553,14 +553,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// down to the max limit while they are still waiting to finish.
mFinishingActivities.remove(r);
- stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY);
+ stopWaitingForActivityVisible(r);
}
+ /** There is no valid launch time, just stop waiting. */
void stopWaitingForActivityVisible(ActivityRecord r) {
- stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r));
+ stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN);
}
- void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) {
+ void stopWaitingForActivityVisible(ActivityRecord r, long totalTime,
+ @WaitResult.LaunchState int launchState) {
boolean changed = false;
for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
final WaitInfo w = mWaitingForActivityVisible.get(i);
@@ -570,6 +572,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
result.timeout = false;
result.who = w.getComponent();
result.totalTime = totalTime;
+ result.launchState = launchState;
mWaitingForActivityVisible.remove(w);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e146adadffe7..758aaa0fee9a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANI
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
@@ -115,7 +116,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -184,7 +184,6 @@ public class AppTransition implements Dump {
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private @TransitionOldType int mNextAppTransitionOld = TRANSIT_OLD_UNSET;
private @TransitionFlags int mNextAppTransitionFlags = 0;
private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
private @TransitionOldType int mLastUsedAppTransition = TRANSIT_OLD_UNSET;
@@ -330,46 +329,20 @@ public class AppTransition implements Dump {
}
boolean isTransitionSet() {
- return mNextAppTransitionOld != TRANSIT_OLD_UNSET || !mNextAppTransitionRequests.isEmpty();
- }
-
- // TODO(new-app-tranistion): Remove this after migrating to new app transition system.
- boolean isTransitionOldEqual(@TransitionOldType int transit) {
- return mNextAppTransitionOld == transit;
+ return !mNextAppTransitionRequests.isEmpty();
}
boolean isUnoccluding() {
- return WindowManagerService.sUseNewAppTransit
- ? mNextAppTransitionRequests.contains(TRANSIT_OLD_KEYGUARD_UNOCCLUDE)
- : mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+ return mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_UNOCCLUDE);
}
- @TransitionOldType
- int getAppTransitionOld() {
- return mNextAppTransitionOld;
- }
-
boolean transferFrom(AppTransition other) {
- prepareAppTransitionOld(other.getAppTransitionOld(), true /* alwaysKeepCurrent */,
- 0 /* flags */, false /* forceOverride */);
mNextAppTransitionRequests.addAll(other.mNextAppTransitionRequests);
return prepare();
}
- private void setAppTransitionOld(@TransitionOldType int transit, int flags) {
- mNextAppTransitionOld = transit;
- mNextAppTransitionFlags |= flags;
- setLastAppTransition(TRANSIT_OLD_UNSET, null, null, null);
- updateBooster();
- if (isTransitionSet()) {
- removeAppTransitionTimeoutCallbacks();
- mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable,
- APP_TRANSITION_TIMEOUT_MS);
- }
- }
-
- void setLastAppTransition(int transit, ActivityRecord openingApp, ActivityRecord closingApp,
- ActivityRecord changingApp) {
+ void setLastAppTransition(@TransitionOldType int transit, ActivityRecord openingApp,
+ ActivityRecord closingApp, ActivityRecord changingApp) {
mLastUsedAppTransition = transit;
mLastOpeningApp = "" + openingApp;
mLastClosingApp = "" + closingApp;
@@ -460,8 +433,7 @@ public class AppTransition implements Dump {
* @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
* layout pass needs to be done
*/
- int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
- mNextAppTransitionOld = TRANSIT_OLD_UNSET;
+ int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) {
mNextAppTransitionFlags = 0;
mNextAppTransitionRequests.clear();
setAppTransitionState(APP_STATE_RUNNING);
@@ -469,7 +441,8 @@ public class AppTransition implements Dump {
topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
- int redoLayout = notifyAppTransitionStartingLocked(transit,
+ int redoLayout = notifyAppTransitionStartingLocked(
+ AppTransition.isKeyguardGoingAwayTransitOld(transit),
topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
? topOpeningAnim.getStatusBarTransitionsStartTime()
@@ -494,7 +467,9 @@ public class AppTransition implements Dump {
}
void freeze() {
- final int transit = mNextAppTransitionOld;
+ final boolean keyguardGoingAway = mNextAppTransitionRequests.contains(
+ TRANSIT_KEYGUARD_GOING_AWAY);
+
// The RemoteAnimationControl didn't register AppTransitionListener and
// only initialized the finish and timeout callback when goodToGo().
// So cancel the remote animation here to prevent the animation can't do
@@ -502,10 +477,10 @@ public class AppTransition implements Dump {
if (mRemoteAnimationController != null) {
mRemoteAnimationController.cancelAnimation("freeze");
}
- setAppTransitionOld(TRANSIT_OLD_UNSET, 0 /* flags */);
+ mNextAppTransitionRequests.clear();
clear();
setReady();
- notifyAppTransitionCancelledLocked(transit);
+ notifyAppTransitionCancelledLocked(keyguardGoingAway);
}
private void setAppTransitionState(int state) {
@@ -524,7 +499,7 @@ public class AppTransition implements Dump {
private boolean needsBoosting() {
final boolean recentsAnimRunning = mService.getRecentsAnimationController() != null;
- return mNextAppTransitionOld != TRANSIT_OLD_UNSET
+ return !mNextAppTransitionRequests.isEmpty()
|| mAppTransitionState == APP_STATE_READY
|| mAppTransitionState == APP_STATE_RUNNING
|| recentsAnimRunning;
@@ -550,9 +525,9 @@ public class AppTransition implements Dump {
}
}
- private void notifyAppTransitionCancelledLocked(int transit) {
+ private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) {
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onAppTransitionCancelledLocked(transit);
+ mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway);
}
}
@@ -562,12 +537,12 @@ public class AppTransition implements Dump {
}
}
- private int notifyAppTransitionStartingLocked(int transit, long duration,
+ private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
int redoLayout = 0;
for (int i = 0; i < mListeners.size(); i++) {
- redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(transit, duration,
- statusBarAnimationStartTime, statusBarAnimationDuration);
+ redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ duration, statusBarAnimationStartTime, statusBarAnimationDuration);
}
return redoLayout;
}
@@ -1578,7 +1553,7 @@ public class AppTransition implements Dump {
&& !mNextAppTransitionOverrideRequested
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL
- && mNextAppTransitionOld != TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+ && !mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY);
}
RemoteAnimationController getRemoteAnimationController() {
@@ -1616,7 +1591,7 @@ public class AppTransition implements Dump {
}
Animation a;
- if (isKeyguardGoingAwayTransit(transit) && enter) {
+ if (isKeyguardGoingAwayTransitOld(transit) && enter) {
a = loadKeyguardExitAnimation(transit);
} else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
a = null;
@@ -1645,9 +1620,8 @@ public class AppTransition implements Dump {
} else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) {
a = createRelaunchAnimation(frame, insets);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", a,
- mNextAppTransitionOld, appTransitionOldToString(transit),
- Debug.getCallers(3));
+ "applyAnimation: anim=%s transit=%s Callers=%s", a,
+ appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1799,14 +1773,12 @@ public class AppTransition implements Dump {
int getAppStackClipMode() {
return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
|| mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY)
- || mNextAppTransitionOld == TRANSIT_OLD_ACTIVITY_RELAUNCH
|| mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
- || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY
- || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
? STACK_CLIP_NONE
: STACK_CLIP_AFTER_ANIM;
}
+ @TransitionFlags
public int getTransitFlags() {
return mNextAppTransitionFlags;
}
@@ -2001,9 +1973,7 @@ public class AppTransition implements Dump {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("mNextAppTransition=");
- sb.append(appTransitionOldToString(mNextAppTransitionOld));
- sb.append(", mNextAppTransitionRequests=[");
+ sb.append("mNextAppTransitionRequests=[");
boolean separator = false;
for (Integer transit : mNextAppTransitionRequests) {
@@ -2194,6 +2164,8 @@ public class AppTransition implements Dump {
"TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION"));
sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED,
"TRANSIT_FLAG_APP_CRASHED"));
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_OPEN_BEHIND,
+ "TRANSIT_FLAG_OPEN_BEHIND"));
}
/**
@@ -2291,47 +2263,6 @@ public class AppTransition implements Dump {
mCurrentUserId = newUserId;
}
- /**
- * @return true if transition is not running and should not be skipped, false if transition is
- * already running
- */
- boolean prepareAppTransitionOld(@TransitionOldType int transit, boolean alwaysKeepCurrent,
- @TransitionFlags int flags, boolean forceOverride) {
- if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- return false;
- }
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d "
- + "Callers=%s",
- appTransitionOldToString(transit), this, alwaysKeepCurrent,
- mDisplayContent.getDisplayId(), Debug.getCallers(5));
- final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransitionOld)
- && transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
- if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
- || mNextAppTransitionOld == TRANSIT_OLD_NONE || allowSetCrashing) {
- setAppTransitionOld(transit, flags);
- }
- // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
- // relies on the fact that we always execute a Keyguard transition after preparing one. We
- // also don't want to change away from a crashing transition.
- else if (!alwaysKeepCurrent && !isKeyguardTransit(mNextAppTransitionOld)
- && mNextAppTransitionOld != TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
- if (transit == TRANSIT_OLD_TASK_OPEN && isTransitionOldEqual(TRANSIT_OLD_TASK_CLOSE)) {
- // Opening a new task always supersedes a close for the anim.
- setAppTransitionOld(transit, flags);
- } else if (transit == TRANSIT_OLD_ACTIVITY_OPEN
- && isTransitionOldEqual(TRANSIT_OLD_ACTIVITY_CLOSE)) {
- // Opening a new activity always supersedes a close for the anim.
- setAppTransitionOld(transit, flags);
- } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransitionOld)) {
- // Task animations always supersede activity animations, because if we have both, it
- // usually means that activity transition were just trampoline activities.
- setAppTransitionOld(transit, flags);
- }
- }
- return prepare();
- }
-
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
return false;
@@ -2349,39 +2280,39 @@ public class AppTransition implements Dump {
* @return true if {@param transit} is representing a transition in which Keyguard is going
* away, false otherwise
*/
- public static boolean isKeyguardGoingAwayTransit(int transit) {
+ public static boolean isKeyguardGoingAwayTransitOld(int transit) {
return transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
|| transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
}
- static boolean isKeyguardTransit(@TransitionOldType int transit) {
- return isKeyguardGoingAwayTransit(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ static boolean isKeyguardTransitOld(@TransitionOldType int transit) {
+ return isKeyguardGoingAwayTransitOld(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
|| transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
- static boolean isTaskTransit(@TransitionOldType int transit) {
- return isTaskOpenTransit(transit)
+ static boolean isTaskTransitOld(@TransitionOldType int transit) {
+ return isTaskOpenTransitOld(transit)
|| transit == TRANSIT_OLD_TASK_CLOSE
|| transit == TRANSIT_OLD_TASK_TO_BACK;
}
- private static boolean isTaskOpenTransit(@TransitionOldType int transit) {
+ private static boolean isTaskOpenTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_TASK_OPEN
|| transit == TRANSIT_OLD_TASK_OPEN_BEHIND
|| transit == TRANSIT_OLD_TASK_TO_FRONT;
}
- static boolean isActivityTransit(@TransitionOldType int transit) {
+ static boolean isActivityTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_ACTIVITY_OPEN
|| transit == TRANSIT_OLD_ACTIVITY_CLOSE
|| transit == TRANSIT_OLD_ACTIVITY_RELAUNCH;
}
- static boolean isChangeTransit(@TransitionOldType int transit) {
+ static boolean isChangeTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
}
- static boolean isClosingTransit(@TransitionOldType int transit) {
+ static boolean isClosingTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_ACTIVITY_CLOSE
|| transit == TRANSIT_OLD_TASK_CLOSE
|| transit == TRANSIT_OLD_WALLPAPER_CLOSE
@@ -2390,6 +2321,51 @@ public class AppTransition implements Dump {
|| transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
}
+ static boolean isNormalTransit(@TransitionType int transit) {
+ return transit == TRANSIT_OPEN
+ || transit == TRANSIT_CLOSE
+ || transit == TRANSIT_TO_FRONT
+ || transit == TRANSIT_TO_BACK;
+ }
+
+ static boolean isKeyguardTransit(@TransitionType int transit) {
+ return transit == TRANSIT_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
+ }
+
+ @TransitionType int getKeyguardTransition() {
+ // In case we unocclude Keyguard and occlude it again, meaning that we never actually
+ // unoccclude/occlude Keyguard, but just run a normal transition.
+ final int occludeIndex = mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_UNOCCLUDE);
+ if (occludeIndex != -1
+ && occludeIndex < mNextAppTransitionRequests.indexOf(TRANSIT_KEYGUARD_OCCLUDE)) {
+ return TRANSIT_NONE;
+ }
+
+ for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
+ final @TransitionType int transit = mNextAppTransitionRequests.get(i);
+ if (isKeyguardTransit(transit)) {
+ return transit;
+ }
+ }
+ return TRANSIT_NONE;
+ }
+
+ @TransitionType int getFirstAppTransition() {
+ for (int i = 0; i < mNextAppTransitionRequests.size(); ++i) {
+ final @TransitionType int transit = mNextAppTransitionRequests.get(i);
+ if (transit != TRANSIT_NONE && !isKeyguardTransit(transit)) {
+ return transit;
+ }
+ }
+ return TRANSIT_NONE;
+ }
+
+ boolean containsTransitRequest(@TransitionType int transit) {
+ return mNextAppTransitionRequests.contains(transit);
+ }
+
/**
* @return whether the transition should show the thumbnail being scaled down.
*/
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 069f962111db..64cbb0dec635 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -17,19 +17,31 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
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 static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
@@ -38,6 +50,10 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -46,7 +62,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_L
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
+import static com.android.server.wm.AppTransition.isNormalTransit;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -54,6 +70,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -63,7 +80,9 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
@@ -97,6 +116,30 @@ public class AppTransitionController {
}
/**
+ * Returns the currently visible window that is associated with the wallpaper in case we are
+ * transitioning from an activity with a wallpaper to one without.
+ */
+ private @Nullable WindowState getOldWallpaper() {
+ final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
+ final @TransitionType int firstTransit =
+ mDisplayContent.mAppTransition.getFirstAppTransition();
+
+ final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */);
+ final boolean showWallpaper = wallpaperTarget != null
+ && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+ // Update task open transition to wallpaper transition when wallpaper is visible.
+ // (i.e.launching app info activity from recent tasks)
+ || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT)
+ && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null)
+ && mWallpaperControllerLocked.isWallpaperVisible()));
+ // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
+ // don't consider upgrading to wallpaper transition.
+ return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
+ ? null : wallpaperTarget;
+ }
+
+ /**
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
@@ -109,13 +152,8 @@ public class AppTransitionController {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
- // TODO(new-app-transition): Compute the best transition from
- // appTransition.mNextAppTransitionRequests
+ // TODO(new-app-transition): Remove code using appTransition.getAppTransition()
final AppTransition appTransition = mDisplayContent.mAppTransition;
- int transit = appTransition.getAppTransitionOld();
- if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
- transit = WindowManager.TRANSIT_OLD_UNSET;
- }
mDisplayContent.mSkipAppTransitionAnimation = false;
mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
@@ -147,17 +185,18 @@ public class AppTransitionController {
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
- // Determine if closing and opening app token sets are wallpaper targets, in which case
- // special animations are needed.
- final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
- final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
- && hasWallpaperTarget;
- final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
- && hasWallpaperTarget;
+ final @TransitionOldType int transit = getTransitCompatType(
+ mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper());
- transit = maybeUpdateTransitToTranslucentAnim(transit);
- transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
- closingAppHasWallpaper);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "handleAppTransitionReady: displayId=%d appTransition={%s}"
+ + " openingApps=[%s] closingApps=[%s] transit=%s",
+ mDisplayContent.mDisplayId,
+ appTransition.toString(),
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ AppTransition.appTransitionOldToString(transit));
// Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme. If all closing windows are obscured, then there is
@@ -191,8 +230,7 @@ public class AppTransitionController {
topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
- layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
- mDisplayContent.mOpeningApps);
+ layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
@@ -222,6 +260,162 @@ public class AppTransitionController {
layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
+ /**
+ * Get old transit type based on the current transit requests.
+ *
+ * @param appTransition {@link AppTransition} for managing app transition state.
+ * @param openingApps {@link ActivityRecord}s which are becoming visible.
+ * @param closingApps {@link ActivityRecord}s which are becoming invisible.
+ * @param wallpaperTarget If non-null, this is the currently visible window that is associated
+ * with the wallpaper.
+ * @param oldWallpaper The currently visible window that is associated with the wallpaper in
+ * case we are transitioning from an activity with a wallpaper to one
+ * without. Otherwise null.
+ */
+ static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
+ ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
+ @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper) {
+
+ // Determine if closing and opening app token sets are wallpaper targets, in which case
+ // special animations are needed.
+ final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
+ && wallpaperTarget != null;
+ final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
+ && wallpaperTarget != null;
+
+ // Keyguard transit has highest priority.
+ switch (appTransition.getKeyguardTransition()) {
+ case TRANSIT_KEYGUARD_GOING_AWAY:
+ return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+ : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+ case TRANSIT_KEYGUARD_OCCLUDE:
+ // When there is a closing app, the keyguard has already been occluded by an
+ // activity, and another activity has started on top of that activity, so normal
+ // app transition animation should be used.
+ return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
+ : TRANSIT_OLD_ACTIVITY_OPEN;
+ case TRANSIT_KEYGUARD_UNOCCLUDE:
+ return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+ }
+
+ final @TransitionFlags int flags = appTransition.getTransitFlags();
+ final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
+
+ // Special transitions
+ // TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
+ if (appTransition.containsTransitRequest(TRANSIT_CHANGE_WINDOWING_MODE)) {
+ return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+ }
+ if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
+ return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+ }
+ if (firstTransit == TRANSIT_NONE) {
+ return TRANSIT_OLD_NONE;
+ }
+
+ /*
+ * There are cases where we open/close a new task/activity, but in reality only a
+ * translucent activity on top of existing activities is opening/closing. For that one, we
+ * have a different animation because non of the task/activity animations actually work well
+ * with translucent apps.
+ */
+ if (isNormalTransit(firstTransit)) {
+ boolean allOpeningVisible = true;
+ boolean allTranslucentOpeningApps = !openingApps.isEmpty();
+ for (int i = openingApps.size() - 1; i >= 0; i--) {
+ final ActivityRecord activity = openingApps.valueAt(i);
+ if (!activity.isVisible()) {
+ allOpeningVisible = false;
+ if (activity.fillsParent()) {
+ allTranslucentOpeningApps = false;
+ }
+ }
+ }
+ boolean allTranslucentClosingApps = !closingApps.isEmpty();
+ for (int i = closingApps.size() - 1; i >= 0; i--) {
+ if (closingApps.valueAt(i).fillsParent()) {
+ allTranslucentClosingApps = false;
+ break;
+ }
+ }
+
+ if (allTranslucentClosingApps && allOpeningVisible) {
+ return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
+ }
+ if (allTranslucentOpeningApps && closingApps.isEmpty()) {
+ return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+ }
+ }
+
+ final ActivityRecord topOpeningApp = getTopApp(openingApps,
+ false /* ignoreHidden */);
+ final ActivityRecord topClosingApp = getTopApp(closingApps,
+ true /* ignoreHidden */);
+
+ if (closingAppHasWallpaper && openingAppHasWallpaper) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
+ switch (firstTransit) {
+ case TRANSIT_OPEN:
+ return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+ case TRANSIT_CLOSE:
+ return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+ }
+ } else if (oldWallpaper != null && !openingApps.isEmpty()
+ && !openingApps.contains(oldWallpaper.mActivityRecord)
+ && closingApps.contains(oldWallpaper.mActivityRecord)
+ && topClosingApp == oldWallpaper.mActivityRecord) {
+ // We are transitioning from an activity with a wallpaper to one without.
+ return TRANSIT_OLD_WALLPAPER_CLOSE;
+ } else if (wallpaperTarget != null && wallpaperTarget.isVisible()
+ && openingApps.contains(wallpaperTarget.mActivityRecord)
+ && topOpeningApp == wallpaperTarget.mActivityRecord
+ /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ return TRANSIT_OLD_WALLPAPER_OPEN;
+ }
+
+ final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
+ openingApps, closingApps, true /* visible */);
+ final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
+ openingApps, closingApps, false /* visible */);
+ final boolean isActivityOpening = !openingWcs.isEmpty()
+ && openingWcs.valueAt(0).asActivityRecord() != null;
+ final boolean isActivityClosing = !closingWcs.isEmpty()
+ && closingWcs.valueAt(0).asActivityRecord() != null;
+ final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening;
+ final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing;
+
+ if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
+ return TRANSIT_OLD_TASK_TO_FRONT;
+ }
+ if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
+ return TRANSIT_OLD_TASK_TO_BACK;
+ }
+ if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
+ if (isTaskOpening) {
+ return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
+ ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
+ }
+ if (isActivityOpening) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ }
+ }
+ if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
+ if (isTaskClosing) {
+ return TRANSIT_OLD_TASK_CLOSE;
+ }
+ if (isActivityClosing) {
+ return TRANSIT_OLD_ACTIVITY_CLOSE;
+ }
+ }
+ if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH)
+ && !openingWcs.isEmpty() && !openingApps.isEmpty()) {
+ return TRANSIT_OLD_ACTIVITY_RELAUNCH;
+ }
+ return TRANSIT_OLD_NONE;
+ }
+
private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
return mainWindow != null ? mainWindow.mAttrs : null;
@@ -691,137 +885,6 @@ public class AppTransitionController {
return true;
}
- private int maybeUpdateTransitToWallpaper(@TransitionOldType int transit,
- boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
- // Given no app transition pass it through instead of a wallpaper transition.
- // Never convert the crashing transition.
- // Never convert a change transition since the top activity isn't changing and will likely
- // still be above an opening wallpaper.
- if (transit == TRANSIT_OLD_NONE || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE
- || AppTransition.isChangeTransit(transit)) {
- return transit;
- }
-
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- final boolean showWallpaper = wallpaperTarget != null
- && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
- // Update task open transition to wallpaper transition when wallpaper is visible.
- // (i.e.launching app info activity from recent tasks)
- || ((transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT)
- && mWallpaperControllerLocked.isWallpaperVisible()));
- // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
- // don't consider upgrading to wallpaper transition.
- final WindowState oldWallpaper =
- (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
- ? null
- : wallpaperTarget;
- final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
- final ActivityRecord topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
- false /* ignoreHidden */);
- final ActivityRecord topClosingApp = getTopApp(mDisplayContent.mClosingApps,
- true /* ignoreHidden */);
-
- boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
- wallpaperTarget, oldWallpaper, openingApps, closingApps);
-
- if (openingCanBeWallpaperTarget && transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) {
- transit = TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New transit: %s", AppTransition.appTransitionOldToString(transit));
- }
- // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
- // relies on the fact that we always execute a Keyguard transition after preparing one.
- else if (!isKeyguardGoingAwayTransit(transit)) {
- if (closingAppHasWallpaper && openingAppHasWallpaper) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
- switch (transit) {
- case TRANSIT_OLD_ACTIVITY_OPEN:
- case TRANSIT_OLD_TASK_OPEN:
- case TRANSIT_OLD_TASK_TO_FRONT:
- transit = TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
- break;
- case TRANSIT_OLD_ACTIVITY_CLOSE:
- case TRANSIT_OLD_TASK_CLOSE:
- case TRANSIT_OLD_TASK_TO_BACK:
- transit = TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
- break;
- }
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New transit: %s", AppTransition.appTransitionOldToString(transit));
- } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
- && !openingApps.contains(oldWallpaper.mActivityRecord)
- && closingApps.contains(oldWallpaper.mActivityRecord)
- && topClosingApp == oldWallpaper.mActivityRecord) {
- // We are transitioning from an activity with a wallpaper to one without.
- transit = TRANSIT_OLD_WALLPAPER_CLOSE;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New transit away from wallpaper: %s",
- AppTransition.appTransitionOldToString(transit));
- } else if (wallpaperTarget != null && wallpaperTarget.isVisible()
- && openingApps.contains(wallpaperTarget.mActivityRecord)
- && topOpeningApp == wallpaperTarget.mActivityRecord
- && transit != TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE) {
- // We are transitioning from an activity without
- // a wallpaper to now showing the wallpaper
- transit = TRANSIT_OLD_WALLPAPER_OPEN;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "New transit into wallpaper: %s",
- AppTransition.appTransitionOldToString(transit));
- }
- }
- return transit;
- }
-
- /**
- * There are cases where we open/close a new task/activity, but in reality only a translucent
- * activity on top of existing activities is opening/closing. For that one, we have a different
- * animation because non of the task/activity animations actually work well with translucent
- * apps.
- *
- * @param transit The current transition type.
- * @return The current transition type or
- * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE}/
- * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
- * situation.
- */
- @VisibleForTesting
- int maybeUpdateTransitToTranslucentAnim(@TransitionOldType int transit) {
- if (AppTransition.isChangeTransit(transit)) {
- // There's no special animation to handle change animations with translucent apps
- return transit;
- }
- final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
- || AppTransition.isActivityTransit(transit);
- boolean allOpeningVisible = true;
- boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
- for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mDisplayContent.mOpeningApps.valueAt(i);
- if (!activity.isVisible()) {
- allOpeningVisible = false;
- if (activity.fillsParent()) {
- allTranslucentOpeningApps = false;
- }
- }
- }
- boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty();
- for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
- if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) {
- allTranslucentClosingApps = false;
- break;
- }
- }
-
- if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
- return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
- }
- if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
- return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
- }
- return transit;
- }
-
/**
* Identifies whether the current transition occurs within a single task or not. This is used
* to determine whether animations should be clipped to the task bounds instead of stack bounds.
@@ -855,7 +918,7 @@ public class AppTransitionController {
return true;
}
- private boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
+ private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
return true;
@@ -873,7 +936,7 @@ public class AppTransitionController {
* {@link ActivityRecord#isVisible}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
+ private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
boolean ignoreInvisible) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 388422e19090..097f4ed97e15 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -73,9 +73,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
@@ -627,12 +626,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mInEnsureActivitiesVisible = false;
- /**
- * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to
- * prevent duplicate requests to input.
- */
- WindowState mLastRequestedFocus = null;
-
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -4547,22 +4540,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
- boolean alwaysKeepCurrent) {
- prepareAppTransitionOld(transit, alwaysKeepCurrent, 0 /* flags */,
- false /* forceOverride */);
- }
-
- void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
- boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
- boolean forceOverride) {
- final boolean prepared = mAppTransition.prepareAppTransitionOld(
- transit, alwaysKeepCurrent, flags, forceOverride);
- if (prepared && okToAnimate()) {
- mSkipAppTransitionAnimation = false;
- }
- }
-
void prepareAppTransition(@WindowManager.TransitionType int transit) {
prepareAppTransition(transit, 0 /* flags */);
}
@@ -4641,10 +4618,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
- final int transit = mAppTransition.getAppTransitionOld();
- return transit == TRANSIT_OLD_ACTIVITY_OPEN
- || transit == TRANSIT_OLD_TASK_OPEN
- || transit == TRANSIT_OLD_TASK_TO_FRONT;
+ return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
+ || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
}
/**
@@ -5607,7 +5582,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- public void onAppTransitionCancelledLocked(int transit) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
continueUpdateOrientationForDiffOrienLaunchingApp();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e4f92565db9b..f14a2ee8e7ee 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -601,7 +601,7 @@ public class DisplayPolicy {
}
@Override
- public int onAppTransitionStartingLocked(int transit, long duration,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -614,7 +614,7 @@ public class DisplayPolicy {
}
@Override
- public void onAppTransitionCancelledLocked(int transit) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
mHandler.post(mAppTransitionCancelled);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e7d8ad690bc4..ad4e64a08183 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -45,7 +45,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -64,6 +63,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
new file mode 100644
index 000000000000..d00faef1c147
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.attestation.IImpressionAttestationService;
+import android.service.attestation.ImpressionAttestationService;
+import android.service.attestation.ImpressionToken;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
+
+/**
+ * Handles requests into {@link ImpressionAttestationService}
+ *
+ * Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
+ * blocking calls into another service.
+ */
+public class ImpressionAttestationController {
+ private static final String TAG = "ImpressionAttestationController";
+ private static final boolean DEBUG = false;
+
+ private final Object mServiceConnectionLock = new Object();
+
+ @GuardedBy("mServiceConnectionLock")
+ private ImpressionAttestationServiceConnection mServiceConnection;
+
+ private final Context mContext;
+
+ /**
+ * Lock used for the cached {@link #mImpressionAlgorithms} array
+ */
+ private final Object mImpressionAlgorithmsLock = new Object();
+
+ @GuardedBy("mImpressionAlgorithmsLock")
+ private String[] mImpressionAlgorithms;
+
+ private final Handler mHandler;
+
+ private interface Command {
+ void run(IImpressionAttestationService service) throws RemoteException;
+ }
+
+ ImpressionAttestationController(Context context) {
+ mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ String[] getSupportedImpressionAlgorithms() {
+ // We have a separate lock for the impression algorithm array since it doesn't need to make
+ // the request through the service connection. Instead, we have a lock to ensure we can
+ // properly cache the impression algorithms array so we don't need to call into the
+ // ExtServices process for each request.
+ synchronized (mImpressionAlgorithmsLock) {
+ // Already have cached values
+ if (mImpressionAlgorithms != null) {
+ return mImpressionAlgorithms;
+ }
+
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final PackageManager pm = mContext.getPackageManager();
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Error getting application resources for " + serviceInfo, e);
+ return null;
+ }
+
+ final int resourceId = serviceInfo.metaData.getInt(
+ SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
+ mImpressionAlgorithms = res.getStringArray(resourceId);
+
+ return mImpressionAlgorithms;
+ }
+ }
+
+ int verifyImpressionToken(ImpressionToken impressionToken) {
+ final SyncCommand syncCommand = new SyncCommand();
+ Bundle results = syncCommand.run((service, remoteCallback) -> {
+ try {
+ service.verifyImpressionToken(impressionToken, remoteCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+ }
+ });
+
+ return results.getInt(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+ }
+
+ ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+ String hashAlgorithm) {
+ final SyncCommand syncCommand = new SyncCommand();
+ Bundle results = syncCommand.run((service, remoteCallback) -> {
+ try {
+ service.generateImpressionToken(screenshot, bounds, hashAlgorithm, remoteCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+ }
+ });
+
+ return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+ }
+
+ /**
+ * Run a command, starting the service connection if necessary.
+ */
+ private void connectAndRun(@NonNull Command command) {
+ synchronized (mServiceConnectionLock) {
+ mHandler.resetTimeoutMessage();
+ if (mServiceConnection == null) {
+ if (DEBUG) Slog.v(TAG, "creating connection");
+
+ // Create the connection
+ mServiceConnection = new ImpressionAttestationServiceConnection();
+
+ final ComponentName component = getServiceComponentName();
+ if (DEBUG) Slog.v(TAG, "binding to: " + component);
+ if (component != null) {
+ final Intent intent = new Intent();
+ intent.setComponent(component);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindServiceAsUser(intent, mServiceConnection,
+ Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
+ if (DEBUG) Slog.v(TAG, "bound");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ mServiceConnection.runCommandLocked(command);
+ }
+ }
+
+ @Nullable
+ private ServiceInfo getServiceInfo() {
+ final String packageName =
+ mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
+ if (packageName == null) {
+ Slog.w(TAG, "no external services package!");
+ return null;
+ }
+
+ final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ Slog.w(TAG, "No valid components found.");
+ return null;
+ }
+ return resolveInfo.serviceInfo;
+ }
+
+ @Nullable
+ private ComponentName getServiceComponentName() {
+ final ServiceInfo serviceInfo = getServiceInfo();
+ if (serviceInfo == null) return null;
+
+ final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+ .equals(serviceInfo.permission)) {
+ Slog.w(TAG, name.flattenToShortString() + " requires permission "
+ + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+ return null;
+ }
+
+ if (DEBUG) Slog.v(TAG, "getServiceComponentName(): " + name);
+ return name;
+ }
+
+ private class SyncCommand {
+ private static final int WAIT_TIME_S = 5;
+ private Bundle mResult;
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+ public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+ connectAndRun(service -> {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ mResult = result;
+ mCountDownLatch.countDown();
+ });
+ func.accept(service, callback);
+ });
+
+ try {
+ mCountDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to wait for command", e);
+ }
+
+ return mResult;
+ }
+ }
+
+ private class ImpressionAttestationServiceConnection implements ServiceConnection {
+ @GuardedBy("mServiceConnectionLock")
+ private IImpressionAttestationService mRemoteService;
+
+ @GuardedBy("mServiceConnectionLock")
+ private ArrayList<Command> mQueuedCommands;
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
+ synchronized (mServiceConnectionLock) {
+ mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+ if (mQueuedCommands != null) {
+ final int size = mQueuedCommands.size();
+ if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
+ for (int i = 0; i < size; i++) {
+ final Command queuedCommand = mQueuedCommands.get(i);
+ try {
+ if (DEBUG) Slog.v(TAG, "running queued command #" + i);
+ queuedCommand.run(mRemoteService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception calling " + name + ": " + e);
+ }
+ }
+ mQueuedCommands = null;
+ } else if (DEBUG) {
+ Slog.d(TAG, "no queued commands");
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Slog.v(TAG, "onServiceDisconnected(): " + name);
+ synchronized (mServiceConnectionLock) {
+ mRemoteService = null;
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (DEBUG) Slog.v(TAG, "onBindingDied(): " + name);
+ synchronized (mServiceConnectionLock) {
+ mRemoteService = null;
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ if (DEBUG) Slog.v(TAG, "onNullBinding(): " + name);
+ synchronized (mServiceConnectionLock) {
+ mRemoteService = null;
+ }
+ }
+
+ /**
+ * Only call while holding {@link #mServiceConnectionLock}
+ */
+ private void runCommandLocked(Command command) {
+ if (mRemoteService == null) {
+ if (DEBUG) Slog.d(TAG, "service is null; queuing command");
+ if (mQueuedCommands == null) {
+ mQueuedCommands = new ArrayList<>(1);
+ }
+ mQueuedCommands.add(command);
+ } else {
+ try {
+ if (DEBUG) Slog.v(TAG, "running command right away");
+ command.run(mRemoteService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception calling service: " + e);
+ }
+ }
+ }
+ }
+
+ private class Handler extends android.os.Handler {
+ static final long SERVICE_SHUTDOWN_TIMEOUT_MILLIS = 10000; // 10s
+ static final int MSG_SERVICE_SHUTDOWN_TIMEOUT = 1;
+
+ Handler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SERVICE_SHUTDOWN_TIMEOUT) {
+ if (DEBUG) {
+ Slog.v(TAG, "Shutting down service");
+ }
+ synchronized (mServiceConnectionLock) {
+ if (mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Set a timer for {@link #SERVICE_SHUTDOWN_TIMEOUT_MILLIS} so we can tear down the service
+ * if it's inactive. The requests will be coming from apps so it's hard to tell how often
+ * the requests can come in. Therefore, we leave the service running if requests continue
+ * to come in. Once there's been no activity for 10s, we can shut down the service and
+ * restart when we get a new request.
+ */
+ void resetTimeoutMessage() {
+ if (DEBUG) {
+ Slog.v(TAG, "Reset shutdown message");
+ }
+ removeMessages(MSG_SERVICE_SHUTDOWN_TIMEOUT);
+ sendEmptyMessageDelayed(MSG_SERVICE_SHUTDOWN_TIMEOUT, SERVICE_SHUTDOWN_TIMEOUT_MILLIS);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 457df4e47689..efd9e2a9ddd2 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -73,8 +73,8 @@ import java.util.function.Consumer;
final class InputMonitor {
private final WindowManagerService mService;
- // Current window with input focus for keys and other non-touch events. May be null.
- private WindowState mInputFocus;
+ // Current input focus token for keys and other non-touch events. May be null.
+ private IBinder mInputFocus = null;
// When true, need to call updateInputWindowsLw().
private boolean mUpdateInputWindowsNeeded = true;
@@ -377,31 +377,62 @@ final class InputMonitor {
}
/**
- * Called when the current input focus changes.
+ * Called when the current input focus changes. Will apply it in next updateInputWindows.
* Layer assignment is assumed to be complete by the time this is called.
*/
- public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
+ void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
newWindow, mDisplayId);
+ final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null;
+ if (focus == mInputFocus) {
+ return;
+ }
- if (newWindow != mInputFocus) {
- if (newWindow != null && newWindow.canReceiveKeys()) {
- // Displaying a window implicitly causes dispatching to be unpaused.
- // This is to protect against bugs if someone pauses dispatching but
- // forgets to resume.
- newWindow.mToken.paused = false;
- }
+ if (newWindow != null && newWindow.canReceiveKeys()) {
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newWindow.mToken.paused = false;
+ }
- mInputFocus = newWindow;
- setUpdateInputWindowsNeededLw();
+ setUpdateInputWindowsNeededLw();
- if (updateInputWindows) {
- updateInputWindowsLw(false /*force*/);
- }
+ if (updateInputWindows) {
+ updateInputWindowsLw(false /*force*/);
+ }
+ }
+
+ /**
+ * Called when the current input focus changes.
+ */
+ private void updateInputFocusRequest() {
+ final WindowState focus = mDisplayContent.mCurrentFocus;
+ final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
+
+ if (focusToken == null) {
+ mInputFocus = null;
+ return;
+ }
+
+ if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) {
+ Slog.v(TAG_WM, "Focus not requested for window=%" + focus
+ + " because it has no surface or is not focusable.");
+ mInputFocus = null;
+ return;
+ }
+
+ if (focusToken == mInputFocus) {
+ return;
}
+
+ mInputFocus = focusToken;
+ mInputTransaction.setFocusedWindow(mInputFocus, mDisplayId);
+ EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus,
+ "reason=UpdateInputWindows");
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
}
- public void setFocusedAppLw(ActivityRecord newApp) {
+ void setFocusedAppLw(ActivityRecord newApp) {
// Focused app has changed.
mService.mInputManager.setFocusedApplication(mDisplayId,
newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
@@ -482,30 +513,6 @@ final class InputMonitor {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- private void updateInputFocusRequest() {
- if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) {
- return;
- }
-
- final WindowState focus = mDisplayContent.mCurrentFocus;
- if (focus == null || focus.mInputChannelToken == null) {
- mDisplayContent.mLastRequestedFocus = focus;
- return;
- }
-
- if (!focus.mWinAnimator.hasSurface()) {
- Slog.v(TAG_WM, "Focus not requested for window=%" + focus
- + " because it has no surface.");
- return;
- }
-
- mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId);
- EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
- "Focus request " + focus, "reason=UpdateInputWindows");
- mDisplayContent.mLastRequestedFocus = focus;
- ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
- }
-
@Override
public void accept(WindowState w) {
final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 9339f3475684..7a4d13c2d697 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -63,6 +63,10 @@ class InputWindowHandleWrapper {
return mHandle.displayId;
}
+ boolean isFocusable() {
+ return mHandle.focusable;
+ }
+
InputApplicationHandle getInputApplicationHandle() {
return mHandle.inputApplicationHandle;
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 7b600923eb9b..c3b6149482e2 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -26,18 +26,14 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
@@ -211,11 +207,7 @@ class KeyguardController {
1 /* keyguardGoingAway */,
"keyguardGoingAway");
mRootWindowContainer.getDefaultDisplay()
- .prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
- false /* alwaysKeepCurrent */, convertTransitFlags(flags),
- false /* forceOverride */);
- mRootWindowContainer.getDefaultDisplay()
- .requestTransitionAndLegacyPrepare(TRANSIT_KEYGUARD_GOING_AWAY,
+ .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
convertTransitFlags(flags));
updateKeyguardSleepToken();
@@ -363,10 +355,6 @@ class KeyguardController {
mService.deferWindowLayout();
try {
mRootWindowContainer.getDefaultDisplay()
- .prepareAppTransitionOld(resolveOccludeTransit(),
- false /* alwaysKeepCurrent */, 0 /* flags */,
- true /* forceOverride */);
- mRootWindowContainer.getDefaultDisplay()
.prepareAppTransition(
isDisplayOccluded(DEFAULT_DISPLAY)
? TRANSIT_KEYGUARD_OCCLUDE
@@ -397,10 +385,7 @@ class KeyguardController {
// we immediately dismiss the Keyguard so the activity gets shown without a flicker.
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
if (mKeyguardShowing && canDismissKeyguard()
- && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
- dc.prepareAppTransitionOld(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
- 0 /* flags */, true /* forceOverride */);
- dc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE);
+ && dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
mWindowManager.executeAppTransition();
}
}
@@ -432,28 +417,6 @@ class KeyguardController {
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
- private int resolveOccludeTransit() {
- // TODO(new-app-transition): Remove after migrating to the enw transit system.
- final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- if (mBeforeUnoccludeTransit != TRANSIT_OLD_UNSET
- && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE
- // TODO(b/113840485): Handle app transition for individual display.
- && isDisplayOccluded(DEFAULT_DISPLAY)) {
-
- // Reuse old transit in case we are occluding Keyguard again, meaning that we never
- // actually occclude/unocclude Keyguard, but just run a normal transition.
- return mBeforeUnoccludeTransit;
- // TODO(b/113840485): Handle app transition for individual display.
- } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
-
- // Save transit in case we dismiss/occlude Keyguard shortly after.
- mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransitionOld();
- return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
- } else {
- return TRANSIT_OLD_KEYGUARD_OCCLUDE;
- }
- }
-
private void dismissMultiWindowModeForTaskIfNeeded(
@Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) {
// If turningScreenOn is true, it means that the visibility state has changed from
diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
index 250785138355..c8558dda171d 100644
--- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
+++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -26,10 +26,10 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.UserTokenWatcher;
import com.android.server.wm.LockTaskController.LockTaskToken;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6d29a1d4eb93..abee032d042a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -152,14 +152,14 @@ public class RecentsAnimationController implements DeathRecipient {
*/
final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(int transit, long duration,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
continueDeferredCancel();
return 0;
}
@Override
- public void onAppTransitionCancelledLocked(int transit) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
continueDeferredCancel();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5e8067074939..a550e150a7a0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -39,9 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
@@ -53,11 +51,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
-import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -66,6 +59,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATE
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
@@ -2181,7 +2179,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Set a transition to ensure that we don't immediately try and update the visibility
// of the activity entering PIP
- r.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
final boolean singleActivity = task.getChildCount() == 1;
@@ -2213,8 +2210,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// to the list of apps being closed, and request its transition to be ran.
final ActivityRecord oldTopActivity = task.getTopMostActivity();
if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
- && task.getDisplayContent().mAppTransition.getAppTransitionOld()
- == TRANSIT_OLD_TASK_TO_BACK) {
+ && task.getDisplayContent().mAppTransition.containsTransitRequest(
+ TRANSIT_TO_BACK)) {
task.getDisplayContent().mClosingApps.add(oldTopActivity);
oldTopActivity.mRequestForceTransition = true;
}
@@ -2801,8 +2798,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
r.detachFromProcess();
- r.mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */);
+ r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
TRANSIT_FLAG_APP_CRASHED);
r.destroyIfPossible("handleAppCrashed");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 99e17917e0b7..3caf86c24380 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -62,17 +62,12 @@ import static android.view.SurfaceControl.METADATA_TASK_ID;
import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -85,12 +80,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMA
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
-import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
-import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -113,6 +102,12 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIB
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
+import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -208,6 +203,7 @@ import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.view.WindowManager.TransitionOldType;
import android.window.ITaskOrganizer;
import com.android.internal.annotations.GuardedBy;
@@ -2356,8 +2352,6 @@ class Task extends WindowContainer<WindowContainer> {
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*/
private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
- false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE);
mDisplayContent.mChangingContainers.add(this);
@@ -2437,13 +2431,13 @@ class Task extends WindowContainer<WindowContainer> {
@VisibleForTesting
boolean isInChangeTransition() {
- return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
+ return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransitOld(mTransit);
}
@Override
public SurfaceControl getFreezeSnapshotTarget() {
- final int transit = mDisplayContent.mAppTransition.getAppTransitionOld();
- if (!AppTransition.isChangeTransit(transit)) {
+ if (!mDisplayContent.mAppTransition.containsTransitRequest(
+ TRANSIT_CHANGE_WINDOWING_MODE)) {
return null;
}
// Skip creating snapshot if this transition is controlled by a remote animator which
@@ -2452,7 +2446,7 @@ class Task extends WindowContainer<WindowContainer> {
activityTypes.add(getActivityType());
final RemoteAnimationAdapter adapter =
mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
- this, transit, activityTypes);
+ this, TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, activityTypes);
if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
return null;
}
@@ -3983,7 +3977,7 @@ class Task extends WindowContainer<WindowContainer> {
@Override
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
- int transit, boolean isVoiceInteraction,
+ @TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final RecentsAnimationController control = mWmService.getRecentsAnimationController();
if (control != null) {
@@ -6151,12 +6145,8 @@ class Task extends WindowContainer<WindowContainer> {
"Prepare close transition: prev=" + prev);
if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransitionOld(
- prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_CLOSE
- : TRANSIT_OLD_TASK_CLOSE, false);
dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
@@ -6165,24 +6155,18 @@ class Task extends WindowContainer<WindowContainer> {
"Prepare open transition: prev=" + prev);
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransitionOld(
- prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_OPEN
- : next.mLaunchTaskBehind ? TRANSIT_OLD_TASK_OPEN_BEHIND
- : TRANSIT_OLD_TASK_OPEN, /* alwaysKeepCurrent */false);
- dc.prepareAppTransition(TRANSIT_OPEN);
+ dc.prepareAppTransition(TRANSIT_OPEN,
+ next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
}
}
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false);
dc.prepareAppTransition(TRANSIT_OPEN);
}
}
@@ -6442,7 +6426,6 @@ class Task extends WindowContainer<WindowContainer> {
"Prepare open transition: starting " + r);
// TODO(shell-transitions): record NO_ANIMATION flag somewhere.
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, keepCurTransition);
dc.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(r);
} else {
@@ -6462,7 +6445,6 @@ class Task extends WindowContainer<WindowContainer> {
transit = TRANSIT_OLD_TASK_OPEN;
}
}
- dc.prepareAppTransitionOld(transit, keepCurTransition);
dc.prepareAppTransition(TRANSIT_OPEN);
mTaskSupervisor.mNoAnimActivities.remove(r);
}
@@ -6601,8 +6583,7 @@ class Task extends WindowContainer<WindowContainer> {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
- mDisplayContent.prepareAppTransitionOld(
- TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.finishIfPossible(reason, false /* oomAdj */);
@@ -6834,9 +6815,8 @@ class Task extends WindowContainer<WindowContainer> {
forAllActivities(ActivityRecord::removeLaunchTickRunnable);
}
- private void updateTransitLocked(@WindowManager.TransitionOldType int transit,
- @WindowManager.TransitionType int transit2, ActivityOptions options,
- boolean forceOverride) {
+ private void updateTransitLocked(@WindowManager.TransitionType int transit,
+ ActivityOptions options) {
if (options != null) {
ActivityRecord r = topRunningActivity();
if (r != null && !r.isState(RESUMED)) {
@@ -6845,9 +6825,7 @@ class Task extends WindowContainer<WindowContainer> {
ActivityOptions.abort(options);
}
}
- mDisplayContent.prepareAppTransitionOld(transit, false,
- 0 /* flags */, forceOverride);
- mDisplayContent.prepareAppTransition(transit2);
+ mDisplayContent.prepareAppTransition(transit);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -6868,8 +6846,7 @@ class Task extends WindowContainer<WindowContainer> {
if (noAnimation) {
ActivityOptions.abort(options);
} else {
- updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT, options,
- false /* forceOverride */);
+ updateTransitLocked(TRANSIT_TO_FRONT, options);
}
return;
}
@@ -6904,14 +6881,11 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
- mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_NONE,
- false /* alwaysKeepCurrent */);
mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
ActivityOptions.abort(options);
} else {
- updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT,
- options, false /* forceOverride */);
+ updateTransitLocked(TRANSIT_TO_FRONT, options);
}
// If a new task is moved to the front, then mark the existing top activity as
@@ -6979,8 +6953,7 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+ tr.mTaskId);
- mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_TO_BACK,
- false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
moveToBack("moveTaskToBackLocked", tr);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index e64c0476db1d..009a7ef9e4f2 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -27,7 +27,6 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.TaskDescription;
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -52,7 +51,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Consumer;
@@ -538,16 +536,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
mTmpTaskInfo.configuration.unset();
task.fillTaskInfo(mTmpTaskInfo);
- boolean changed = lastInfo == null
- || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
- || mTmpTaskInfo.isResizeable != lastInfo.isResizeable
- || !Objects.equals(
- mTmpTaskInfo.positionInParent,
- lastInfo.positionInParent)
- || isLetterboxInfoChanged(lastInfo, mTmpTaskInfo)
- || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
- || mTmpTaskInfo.getWindowingMode() != lastInfo.getWindowingMode()
- || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
+ boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
if (!changed) {
int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
@@ -582,20 +571,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- private boolean isLetterboxInfoChanged(
- final RunningTaskInfo lastInfo, final RunningTaskInfo currentInfo) {
- return !Objects.equals(
- currentInfo.letterboxActivityBounds,
- lastInfo.letterboxActivityBounds)
- || !Objects.equals(
- currentInfo.getConfiguration().windowConfiguration.getBounds(),
- lastInfo.getConfiguration().windowConfiguration.getBounds())
- || !Objects.equals(
- currentInfo.getConfiguration().windowConfiguration.getMaxBounds(),
- lastInfo.getConfiguration().windowConfiguration.getMaxBounds())
- || !Objects.equals(currentInfo.parentBounds, lastInfo.parentBounds);
- }
-
@Override
public WindowContainerToken getImeTarget(int displayId) {
enforceTaskPermission("getImeTarget()");
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index f77b6d2a9b1d..6dfcb4192ff2 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -28,7 +28,6 @@ import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserManagerInternal;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
@@ -36,6 +35,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
import java.io.File;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index ce138674cb93..7d61c1973a2a 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -148,7 +148,7 @@ class WallpaperController {
? w.mActivityRecord.getAnimatingContainer() : null;
final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null
&& animatingContainer.isAnimating(TRANSITION | PARENTS)
- && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit)
+ && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit)
&& (animatingContainer.mTransitFlags
& TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 22dca2611302..b25fbc0e18a3 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -81,6 +81,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.view.WindowManager;
+import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
@@ -250,10 +251,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
boolean mLaunchTaskBehind;
/**
- * If we are running an animation, this determines the transition type. Must be one of
- * {@link AppTransition#TransitionFlags}.
+ * If we are running an animation, this determines the transition type.
*/
- int mTransit;
+ @TransitionOldType int mTransit;
/**
* If we are running an animation, this determines the flags during this animation. Must be a
@@ -2399,8 +2399,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*
* @see #getAnimationAdapter
*/
- boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
+ boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
+ boolean enter, boolean isVoiceInteraction,
+ @Nullable ArrayList<WindowContainer> sources) {
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
@@ -2415,6 +2416,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
if (okToAnimate()) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: transit=%s, enter=%b, wc=%s",
+ AppTransition.appTransitionOldToString(transit), enter, this);
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
} else {
cancelAnimation();
@@ -2437,7 +2441,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @See LocalAnimationAdapter
*/
Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
- int transit, boolean enter, boolean isVoiceInteraction) {
+ @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
final int appStackClipMode = getDisplayContent().mAppTransition.getAppStackClipMode();
@@ -2449,7 +2453,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final RemoteAnimationController controller =
getDisplayContent().mAppTransition.getRemoteAnimationController();
- final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
+ final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
// Delaying animation start isn't compatible with remote animations at all.
@@ -2497,7 +2501,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
resultAdapters = new Pair<>(adapter, null);
mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
- || AppTransition.isClosingTransit(transit);
+ || AppTransition.isClosingTransitOld(transit);
mTransit = transit;
mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
@@ -2508,7 +2512,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
- int transit, boolean isVoiceInteraction,
+ @TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 872980149246..d082778f50ba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -115,9 +115,9 @@ public abstract class WindowManagerInternal {
/**
* Called when a pending app transition gets cancelled.
*
- * @param transit transition type indicating what kind of transition got cancelled
+ * @param keyguardGoingAway true if keyguard going away transition transition got cancelled.
*/
- public void onAppTransitionCancelledLocked(int transit) {}
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {}
/**
* Called when an app transition is timed out.
@@ -127,8 +127,7 @@ public abstract class WindowManagerInternal {
/**
* Called when an app transition gets started
*
- * @param transit transition type indicating what kind of transition gets run, must be one
- * of AppTransition.TRANSIT_* values
+ * @param keyguardGoingAway true if keyguard going away transition is started.
* @param duration the total duration of the transition
* @param statusBarAnimationStartTime the desired start time for all visual animations in
* the status bar caused by this app transition in uptime millis
@@ -140,7 +139,7 @@ public abstract class WindowManagerInternal {
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
*/
- public int onAppTransitionStartingLocked(int transit, long duration,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f768435d1b7f..7c7dd6ff268c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -79,7 +79,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
@@ -423,16 +422,6 @@ public class WindowManagerService extends IWindowManager.Stub
DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
/**
- * Use new app transit framework.
- */
- private static final String USE_NEW_APP_TRANSIT =
- "persist.wm.use_new_app_transit";
- /**
- * @see #USE_NEW_APP_TRANSIT
- */
- static boolean sUseNewAppTransit = SystemProperties.getBoolean(USE_NEW_APP_TRANSIT, false);
-
- /**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
* when the display has different orientation.
*/
@@ -1111,7 +1100,7 @@ public class WindowManagerService extends IWindowManager.Stub
= new WindowManagerInternal.AppTransitionListener() {
@Override
- public void onAppTransitionCancelledLocked(int transit) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
}
@Override
@@ -1923,8 +1912,6 @@ public class WindowManagerService extends IWindowManager.Stub
// animation and piggy-back on existing transition animation infrastructure.
final DisplayContent dc = activity.getDisplayContent();
dc.mOpeningApps.add(activity);
- dc.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH,
- ALWAYS_KEEP_CURRENT, 0 /* flags */, false /* forceOverride */);
dc.prepareAppTransition(TRANSIT_RELAUNCH);
dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
frame.width(), frame.height());
@@ -1940,8 +1927,6 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent dc = activity.getDisplayContent();
if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) {
dc.mOpeningApps.add(activity);
- dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
- false /* forceOverride */);
dc.prepareAppTransition(TRANSIT_NONE);
dc.executeAppTransition();
}
@@ -2827,9 +2812,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- getDefaultDisplayContentLocked().prepareAppTransitionOld(TRANSIT_OLD_NONE,
- false /* alwaysKeepCurrent */,
- 0 /* flags */, false /* forceOverride */);
getDefaultDisplayContentLocked().prepareAppTransition(TRANSIT_NONE);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 20b6b6d135d5..a6466821ff38 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,9 +91,6 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.os.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
-import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
-import static android.os.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Telephony.Carriers.DPC_URI;
@@ -108,6 +105,9 @@ import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
+import static com.android.server.pm.UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -222,8 +222,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
@@ -293,6 +291,8 @@ import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.RestrictionsSet;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserRestrictionsUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -4373,7 +4373,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int adminUser = admin.getUserHandle().getIdentifier();
// Password complexity is only taken into account from DO/PO
if (isDeviceOwner(adminComponent, adminUser)
- || isProfileOwner(adminComponent, adminUser)) {
+ || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) {
maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
}
}
@@ -6216,7 +6216,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getAdminCallerIdentity(comp);
+ final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
@@ -7487,6 +7487,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return who != null && who.equals(profileOwner);
}
+ private boolean isProfileOwnerUncheckedLocked(ComponentName who, int userId) {
+ ensureLocked();
+ final ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ return who != null && who.equals(profileOwner);
+ }
+
/**
* Returns {@code true} if the provided caller identity is of a profile owner.
* @param caller identity of caller.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 9e98fc5ff8b4..8ef69822750f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -30,7 +30,6 @@ import android.os.Environment;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
@@ -46,6 +45,7 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import libcore.io.IoUtils;
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 27b07c76fb0d..21c863dde3f6 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -18,6 +18,7 @@ package com.android.server.pm.test.override
import android.content.ComponentName
import android.content.Context
+import android.content.pm.PackageManager
import android.content.pm.parsing.component.ParsedActivity
import android.os.Binder
import android.os.UserHandle
@@ -31,7 +32,6 @@ import com.android.server.pm.UserManagerService
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
-import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
@@ -45,11 +45,8 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import org.mockito.Mockito
import org.mockito.Mockito.any
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.intThat
import org.mockito.Mockito.never
@@ -321,10 +318,6 @@ class PackageManagerComponentLabelIconOverrideTest {
whenever(this.exists(intThat(matcher))) { true }
whenever(this.isUserUnlockingOrUnlocked(intThat(matcher))) { true }
}
- val mockPermissionManagerService: PermissionManagerServiceInternal = mockThrowOnUnmocked {
- whenever(this.enforceCrossUserPermission(anyInt(), anyInt(), anyBoolean(), anyBoolean(),
- anyString())) { }
- }
val mockActivityTaskManager: ActivityTaskManagerInternal = mockThrowOnUnmocked {
whenever(this.isCallerRecents(anyInt())) { false }
}
@@ -335,15 +328,19 @@ class PackageManagerComponentLabelIconOverrideTest {
val mockContext: Context = mockThrowOnUnmocked {
whenever(this.getString(
com.android.internal.R.string.config_overrideComponentUiPackage)) { VALID_PKG }
+ whenever(this.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+ PackageManager.PERMISSION_GRANTED
+ }
}
val mockInjector: PackageManagerService.Injector = mock {
whenever(this.lock) { Object() }
whenever(this.componentResolver) { mockComponentResolver }
whenever(this.userManagerService) { mockUserManagerService }
- whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService }
whenever(this.settings) { mockSettings }
whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) {
- mockActivityTaskManager}
+ mockActivityTaskManager
+ }
whenever(this.appsFilter) { mockAppsFilter }
whenever(this.context) { mockContext }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 1cdd873860b8..e43a002806ee 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -66,7 +66,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntConsumer;
@@ -129,6 +128,8 @@ public class FullScreenMagnificationGestureHandlerTest {
ScaleChangedListener mMockScaleChangedListener;
@Mock
MagnificationRequestObserver mMagnificationRequestObserver;
+ @Mock
+ WindowMagnificationPromptController mWindowMagnificationPromptController;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -170,7 +171,9 @@ public class FullScreenMagnificationGestureHandlerTest {
@After
public void tearDown() {
+ mMgh.onDestroy();
mFullScreenMagnificationController.unregister(DISPLAY_0);
+ verify(mWindowMagnificationPromptController).onDestroy();
}
@NonNull
@@ -178,7 +181,8 @@ public class FullScreenMagnificationGestureHandlerTest {
boolean detectShortcutTrigger) {
FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
mContext, mFullScreenMagnificationController, mMockScaleChangedListener,
- detectTripleTap, detectShortcutTrigger, DISPLAY_0);
+ detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController,
+ DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -434,6 +438,20 @@ public class FullScreenMagnificationGestureHandlerTest {
returnToNormalFrom(STATE_PANNING);
}
+ @Test
+ public void testZoomedWithTripleTap_invokeShowWindowPromptAction() {
+ goFromStateIdleTo(STATE_ZOOMED);
+
+ verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
+ }
+
+ @Test
+ public void testShortcutTriggered_invokeShowWindowPromptAction() {
+ goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+
+ verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
+ }
+
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
new file mode 100644
index 000000000000..5fd28f57c7c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT;
+
+import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE;
+import static com.android.server.accessibility.magnification.WindowMagnificationPromptController.ACTION_TURN_ON_IN_SETTINGS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowMagnificationPromptController}.
+ */
+public class WindowMagnificationPromptControllerTest {
+
+ private static final int TEST_USER = 0;
+
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Mock
+ private StatusBarManager mStatusBarManager;
+ @Rule
+ public A11yTestableContext mTestableContext = new A11yTestableContext(
+ InstrumentationRegistry.getContext());
+ private ContentResolver mResolver = mTestableContext.getContentResolver();
+ private WindowMagnificationPromptController mWindowMagnificationPromptController;
+ private BroadcastReceiver mReceiver;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableContext.addMockSystemService(NotificationManager.class, mNotificationManager);
+ mTestableContext.addMockSystemService(StatusBarManager.class, mStatusBarManager);
+ setWindowMagnificationPromptSettings(true);
+ mWindowMagnificationPromptController = new WindowMagnificationPromptController(
+ mTestableContext, TEST_USER);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWindowMagnificationPromptController.onDestroy();
+ }
+
+ @Test
+ public void showNotificationIfNeeded_promptSettingsIsOn_showNotification() {
+ mWindowMagnificationPromptController.showNotificationIfNeeded();
+
+ verify(mNotificationManager).notify(eq(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE), any(
+ Notification.class));
+ }
+
+ @Test
+ public void tapTurnOnAction_isShown_cancelNotificationAndLaunchMagnificationSettings() {
+ showNotificationAndAssert();
+
+ final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS);
+ mReceiver.onReceive(mTestableContext, intent);
+
+ verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ verifyLaunchMagnificationSettings();
+ }
+
+ @Test
+ public void tapTurnOnAction_isShown_settingsValueIsFalseAndUnregisterReceiver() {
+ showNotificationAndAssert();
+
+ final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS);
+ mReceiver.onReceive(mTestableContext, intent);
+
+ assertThat(Settings.Secure.getInt(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+ -1)).isEqualTo(0);
+ verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+ }
+
+ @Test
+ public void tapDismissAction_isShown_cancelNotificationAndUnregisterReceiver() {
+ showNotificationAndAssert();
+
+ final Intent intent = new Intent(WindowMagnificationPromptController.ACTION_DISMISS);
+ mReceiver.onReceive(mTestableContext, intent);
+
+ verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+ }
+
+ @Test
+ public void promptSettingsChangeToFalse_isShown_cancelNotificationAndUnregisterReceiver() {
+ showNotificationAndAssert();
+
+ setWindowMagnificationPromptSettings(false);
+
+ verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+ }
+
+ @Test
+ public void onDestroy_isShown_cancelNotificationAndUnregisterReceiver() {
+ showNotificationAndAssert();
+
+ mWindowMagnificationPromptController.onDestroy();
+
+ verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE);
+ verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver);
+ }
+
+ private void verifyLaunchMagnificationSettings() {
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(
+ UserHandle.class);
+ verify(mTestableContext.getSpyContext()).startActivityAsUser(intentCaptor.capture(),
+ bundleCaptor.capture(), userHandleCaptor.capture());
+ assertThat(intentCaptor.getValue().getAction()).isEqualTo(
+ Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+ assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER);
+ verify(mStatusBarManager).collapsePanels();
+ }
+
+ private void showNotificationAndAssert() {
+ mWindowMagnificationPromptController.showNotificationIfNeeded();
+ mReceiver = mWindowMagnificationPromptController.mNotificationActionReceiver;
+ assertThat(mReceiver).isNotNull();
+ }
+
+ private void setWindowMagnificationPromptSettings(boolean enable) {
+ Settings.Secure.putIntForUser(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+ enable ? 1 : 0, TEST_USER);
+ if (mWindowMagnificationPromptController != null) {
+ mWindowMagnificationPromptController.onPromptSettingsValueChanged();
+ }
+ }
+
+ private class A11yTestableContext extends TestableContext {
+
+ private Context mSpyContext;
+
+ A11yTestableContext(Context base) {
+ super(base);
+ mSpyContext = Mockito.mock(Context.class);
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ mSpyContext.startActivityAsUser(intent, options, user);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return mSpyContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ mSpyContext.unregisterReceiver(receiver);
+ }
+
+ Context getSpyContext() {
+ return mSpyContext;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index f45cc3bf462c..74c6a7e36c9d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -71,7 +71,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
@@ -80,6 +79,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.FgThread;
import com.android.server.am.UserState.KeyEvictedCallback;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
new file mode 100644
index 000000000000..efdbda38c01c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.face.SensorProps;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class FaceProviderTest {
+
+ private static final String TAG = "FaceProviderTest";
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+
+ private SensorProps[] mSensorProps;
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ private FaceProvider mFaceProvider;
+
+ private static void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+
+ final SensorProps sensor1 = new SensorProps();
+ sensor1.commonProps = new CommonProps();
+ sensor1.commonProps.sensorId = 0;
+ final SensorProps sensor2 = new SensorProps();
+ sensor2.commonProps = new CommonProps();
+ sensor2.commonProps.sensorId = 1;
+
+ mSensorProps = new SensorProps[] {sensor1, sensor2};
+
+ mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+
+ mFaceProvider = new FaceProvider(mContext, mSensorProps, TAG,
+ mLockoutResetDispatcher);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Test
+ public void halServiceDied_resetsAllSchedulers() {
+ assertEquals(mSensorProps.length, mFaceProvider.getSensorProperties().size());
+
+ // Schedule N operations on each sensor
+ final int numFakeOperations = 10;
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ for (int i = 0; i < numFakeOperations; i++) {
+ final ClientMonitor testMonitor = mock(ClientMonitor.class);
+ when(testMonitor.getFreshDaemon()).thenReturn(new Object());
+ scheduler.scheduleClientMonitor(testMonitor);
+ }
+ }
+
+ waitForIdle();
+ // The right amount of pending and current operations are scheduled
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
+ assertNotNull(scheduler.getCurrentClient());
+ }
+
+ // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+ // serviceDied directly.
+ mFaceProvider.binderDied();
+ waitForIdle();
+
+ // No pending operations, no current operation.
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFaceProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ assertNull(scheduler.getCurrentClient());
+ assertEquals(0, scheduler.getCurrentPendingCount());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 35fc7f09c057..99aab5c7a6af 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.face;
+package com.android.server.biometrics.sensors.face.hidl;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -28,8 +29,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.face.hidl.Face10;
import org.junit.Before;
import org.junit.Test;
@@ -49,6 +50,8 @@ public class Face10Test {
private Context mContext;
@Mock
private UserManager mUserManager;
+ @Mock
+ private BiometricScheduler mScheduler;
private LockoutResetDispatcher mLockoutResetDispatcher;
private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
@@ -68,7 +71,7 @@ public class Face10Test {
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
mFace10 = new Face10(mContext, SENSOR_ID, BiometricManager.Authenticators.BIOMETRIC_STRONG,
mLockoutResetDispatcher, false /* supportsSelfIllumination */,
- 1 /* maxTemplatesAllowed */);
+ 1 /* maxTemplatesAllowed */, mScheduler);
mBinder = new Binder();
}
@@ -78,4 +81,13 @@ public class Face10Test {
0 /* challenge */);
waitForIdle();
}
+
+ @Test
+ public void halServiceDied_resetsScheduler() {
+ // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+ // serviceDied directly.
+ mFace10.serviceDied(0 /* cookie */);
+ waitForIdle();
+ verify(mScheduler).reset();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
new file mode 100644
index 000000000000..624775b775fc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.common.CommonProps;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class FingerprintProviderTest {
+
+ private static final String TAG = "FingerprintProviderTest";
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+
+ private SensorProps[] mSensorProps;
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ private FingerprintProvider mFingerprintProvider;
+
+ private static void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+
+ final SensorProps sensor1 = new SensorProps();
+ sensor1.commonProps = new CommonProps();
+ sensor1.commonProps.sensorId = 0;
+ final SensorProps sensor2 = new SensorProps();
+ sensor2.commonProps = new CommonProps();
+ sensor2.commonProps.sensorId = 1;
+
+ mSensorProps = new SensorProps[] {sensor1, sensor2};
+
+ mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+
+ mFingerprintProvider = new FingerprintProvider(mContext, mSensorProps, TAG,
+ mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Test
+ public void halServiceDied_resetsAllSchedulers() {
+ assertEquals(mSensorProps.length, mFingerprintProvider.getSensorProperties().size());
+
+ // Schedule N operations on each sensor
+ final int numFakeOperations = 10;
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ for (int i = 0; i < numFakeOperations; i++) {
+ final ClientMonitor testMonitor = mock(ClientMonitor.class);
+ when(testMonitor.getFreshDaemon()).thenReturn(new Object());
+ scheduler.scheduleClientMonitor(testMonitor);
+ }
+ }
+
+ waitForIdle();
+ // The right amount of pending and current operations are scheduled
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
+ assertNotNull(scheduler.getCurrentClient());
+ }
+
+ // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+ // serviceDied directly.
+ mFingerprintProvider.binderDied();
+ waitForIdle();
+
+ // No pending operations, no current operation.
+ for (SensorProps prop : mSensorProps) {
+ final BiometricScheduler scheduler =
+ mFingerprintProvider.mSensors.get(prop.commonProps.sensorId).getScheduler();
+ assertNull(scheduler.getCurrentClient());
+ assertEquals(0, scheduler.getCurrentPendingCount());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
new file mode 100644
index 000000000000..b2aeb33039f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.hidl;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class Fingerprint21Test {
+
+ private static final String TAG = "Fingerprint21Test";
+ private static final int SENSOR_ID = 1;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ Fingerprint21.HalResultController mHalResultController;
+ @Mock
+ private BiometricScheduler mScheduler;
+
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ private Fingerprint21 mFingerprint21;
+
+ private static void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getInteger(eq(R.integer.config_fingerprintMaxTemplatesPerUser)))
+ .thenReturn(5);
+
+ mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
+ mFingerprint21 = new Fingerprint21(mContext, mScheduler,
+ new Handler(Looper.getMainLooper()), SENSOR_ID,
+ BiometricManager.Authenticators.BIOMETRIC_WEAK, mLockoutResetDispatcher,
+ mHalResultController);
+ }
+
+ @Test
+ public void halServiceDied_resetsScheduler() {
+ // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
+ // serviceDied directly.
+ mFingerprint21.serviceDied(0 /* cookie */);
+ waitForIdle();
+ verify(mScheduler).reset();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index cbe49eb12f95..904e93b7d8cf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -36,7 +36,6 @@ import android.os.Looper;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.permission.IPermissionManager;
import android.security.KeyChain;
import android.telephony.TelephonyManager;
@@ -51,6 +50,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.UserManagerInternal;
import java.io.File;
import java.io.IOException;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index cb49a519d10a..1d2dcaecf978 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -31,7 +31,6 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.ExceptionUtils;
@@ -39,6 +38,7 @@ import android.util.ExceptionUtils;
import androidx.annotation.NonNull;
import com.android.internal.util.FunctionalUtils;
+import com.android.server.pm.UserManagerInternal;
import org.junit.Assert;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 431cc27a6635..34313b888e48 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -54,7 +54,6 @@ import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.permission.IPermissionManager;
import android.provider.Settings;
import android.security.KeyChain;
@@ -70,6 +69,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.File;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 6f62014f0141..649626492448 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -22,6 +22,7 @@ import static com.android.server.hdmi.HdmiUtils.buildMessage;
import static com.google.common.truth.Truth.assertThat;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.platform.test.annotations.Presubmit;
@@ -103,7 +104,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_basicTv_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_1_4,
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
@@ -113,7 +114,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_basicPlayback_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
- Constants.VERSION_1_4,
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
@@ -123,7 +124,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_basicPlaybackAudioSystem_1_4() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1,
- Constants.VERSION_1_4,
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_b,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK,
HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
@@ -134,7 +135,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_basicTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_2_0,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList());
@@ -144,7 +145,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_remoteControlTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_2_0,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList());
@@ -154,7 +155,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_remoteControlPlayback_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_2_0,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList());
@@ -165,7 +166,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_deviceFeaturesTv_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_2_0,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV,
Lists.newArrayList(Constants.RC_PROFILE_TV_NONE),
Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN));
@@ -176,7 +177,7 @@ public class HdmiCecMessageBuilderTest {
@Test
public void buildReportFeatures_deviceFeaturesPlayback_2_0() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV,
- Constants.VERSION_2_0,
+ HdmiControlManager.HDMI_CEC_VERSION_2_0,
Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE,
Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 2e4bed97dbec..3bfaf7024486 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -470,7 +470,7 @@ public class HdmiControlServiceTest {
mTestLooper.dispatchAll();
HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures(
- Constants.ADDR_PLAYBACK_1, Constants.VERSION_2_0,
+ Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0,
Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM),
mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(),
mMyPlaybackDevice.getDeviceFeatures());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index d44d37e4e2a1..679d6900e47b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -44,7 +44,6 @@ import android.os.FileUtils;
import android.os.IProgressListener;
import android.os.RemoteException;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.security.KeyStore;
@@ -57,6 +56,7 @@ import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 4e1454bd0962..73191dca6093 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -27,7 +27,6 @@ import android.os.Handler;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -35,6 +34,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
+import com.android.server.pm.UserManagerInternal;
import java.io.FileNotFoundException;
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index c4d185cfc7f2..e46ab6b01f0a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -84,7 +84,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.test.InstrumentationTestCase;
import android.test.mock.MockContext;
import android.util.ArrayMap;
@@ -587,7 +586,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
@Override
boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
- return true;
+ return mInjectCheckAccessShortcutsPermission;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 95c881e1d927..4b90a5c4a167 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -47,7 +47,6 @@ import android.os.BaseBundle;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 1ca3c7488f32..fcbb5ed1140c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -46,7 +46,6 @@ import android.content.pm.parsing.ParsingPackage;
import android.content.res.TypedArray;
import android.os.Environment;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c7a05ba68e1e..194ae055e01f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -490,6 +490,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mManager.pushDynamicShortcut(s8);
assertEquals(4, getCallerShortcut("s8").getRank());
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s8"), HANDLE_USER_0,
CACHE_OWNER_0);
});
@@ -1456,6 +1457,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// Cache 1 and 2
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"),
HANDLE_USER_0, CACHE_OWNER_0);
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"),
@@ -1538,6 +1540,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// Cache some, but non long lived shortcuts will be ignored.
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"),
HANDLE_USER_0, CACHE_OWNER_0);
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
@@ -1597,6 +1600,48 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s2");
}
+ public void testCachedShortcuts_accessShortcutsPermission() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
+ makeLongLivedShortcut("s4"))));
+ });
+
+ // s1 is not long lived and will be ignored.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = false;
+ assertExpectException(
+ SecurityException.class, "Caller can't access shortcut information", () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"),
+ HANDLE_USER_0, CACHE_OWNER_0);
+ });
+ // Give ACCESS_SHORTCUTS permission to LAUNCHER_1
+ mInjectCheckAccessShortcutsPermission = true;
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"),
+ HANDLE_USER_0, CACHE_OWNER_0);
+ });
+
+ setCaller(CALLING_PACKAGE_1);
+
+ // Get cached shortcuts
+ assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s2", "s3");
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = false;
+ assertExpectException(
+ SecurityException.class, "Caller can't access shortcut information", () -> {
+ mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
+ HANDLE_USER_0, CACHE_OWNER_0);
+ });
+ // Give ACCESS_SHORTCUTS permission to LAUNCHER_1
+ mInjectCheckAccessShortcutsPermission = true;
+ mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s4"),
+ HANDLE_USER_0, CACHE_OWNER_0);
+ });
+
+ assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
+ }
+
public void testCachedShortcuts_canPassShortcutLimit() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -1609,6 +1654,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// Cache All
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
HANDLE_USER_0, CACHE_OWNER_0);
});
@@ -1808,6 +1854,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
setCaller(LAUNCHER_1);
// Cache some shortcuts. Only long lived shortcuts can get cached.
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser(),
CACHE_OWNER_0);
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser(),
@@ -2009,6 +2056,53 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
+ public void testGetShortcuts_personsFlag() {
+ ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
+ .setShortLabel("label")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setPerson(makePerson("person", "personKey", "personUri"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .build();
+
+ setCaller(CALLING_PACKAGE_1);
+ assertTrue(mManager.setDynamicShortcuts(list(s)));
+
+ setCaller(LAUNCHER_1);
+
+ assertNull(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+ ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()).get(0).getPersons());
+
+ assertNull(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+ ShortcutQuery.FLAG_MATCH_DYNAMIC),
+ getCallingUser()).get(0).getPersons());
+
+ // Using FLAG_GET_PERSONS_DATA should fail without permission
+ mInjectCheckAccessShortcutsPermission = false;
+ assertExpectException(
+ SecurityException.class, "Caller can't access shortcut information", () -> {
+ mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+ ShortcutQuery.FLAG_MATCH_DYNAMIC
+ | ShortcutQuery.FLAG_GET_PERSONS_DATA),
+ getCallingUser());
+ });
+
+ mInjectCheckAccessShortcutsPermission = true;
+ assertEquals("person", mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+ ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA),
+ getCallingUser()).get(0).getPersons()[0].getName());
+
+ assertNull(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 0, CALLING_PACKAGE_1, /* activity =*/ null,
+ ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_GET_PERSONS_DATA
+ | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()).get(0).getPersons());
+ }
+
// TODO resource
public void testGetShortcutInfo() {
// Create shortcuts.
@@ -8740,6 +8834,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0,
filter_any));
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
index 6a2b8e0da2d2..c8a405284468 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
@@ -117,6 +117,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
});
@@ -216,6 +217,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
mTestLooper.getNewExecutor());
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
@@ -242,6 +244,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -274,6 +277,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -301,6 +305,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
@@ -500,6 +505,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
@@ -559,6 +565,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -596,6 +603,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -664,6 +672,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -731,6 +740,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
@@ -799,6 +809,7 @@ public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0,
CACHE_OWNER_0);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 44b202d60644..35c513f3de8e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -27,7 +27,6 @@ import android.os.Looper;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index 5846fc110d15..b0423bfd19af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.fail;
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.os.Looper;
-import android.os.UserManagerInternal;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 2250185cf3d7..4fac9dc391e3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -44,7 +44,6 @@ import android.content.pm.UserInfo.UserInfoFlag;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 8e94544707a8..0b44c59a3e15 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -43,7 +43,6 @@ import android.content.pm.UserInfo;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.support.test.uiautomator.UiDevice;
import android.util.ArrayMap;
import android.util.ArraySet;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
index 36ac5d5a111d..ce6939a9beeb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
@@ -89,7 +89,7 @@ public class BadgeExtractorTest extends UiServiceTestCase {
when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
- PendingIntent.getActivity(mContext, 0, new Intent(), 0),
+ PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE),
Icon.createWithResource("", 0)).build();
int flags = metadata.getFlags();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 338ed066e1e4..f1dc098fccf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.timeout;
import android.app.ActivityOptions;
@@ -53,6 +54,7 @@ import org.mockito.ArgumentMatcher;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
+import java.util.function.ToIntFunction;
/**
* Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -158,6 +160,41 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
verifyNoMoreInteractions(mLaunchObserver);
}
+ @Test
+ public void testLaunchState() {
+ final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+ clearInvocations(mLaunchObserver);
+ onActivityLaunched(mTopActivity);
+ notifyTransitionStarting(mTopActivity);
+ if (doRelaunch) {
+ mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+ }
+ final ActivityMetricsLogger.TransitionInfoSnapshot info =
+ notifyWindowsDrawn(mTopActivity);
+ verifyOnActivityLaunchFinished(mTopActivity);
+ return info.getLaunchState();
+ };
+
+ final WindowProcessController app = mTopActivity.app;
+ // Assume that the process is started (ActivityBuilder has mocked the returned value of
+ // ATMS#getProcessController) but the activity has not attached process.
+ mTopActivity.app = null;
+ assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+
+ mTopActivity.app = app;
+ assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
+
+ assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
+
+ mTopActivity.app = null;
+ doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
+ assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+ }
+
private void onActivityLaunched(ActivityRecord activity) {
onIntentStarted(activity.intent);
notifyActivityLaunched(START_SUCCESS, activity);
@@ -168,15 +205,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testOnActivityLaunchFinished() {
- // Assume that the process is started (ActivityBuilder has mocked the returned value of
- // ATMS#getProcessController) but the activity has not attached process.
- mTopActivity.app = null;
onActivityLaunched(mTopActivity);
notifyTransitionStarting(mTopActivity);
- final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity);
- assertWithMessage("Warm launch").that(info.getLaunchState())
- .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
+ notifyWindowsDrawn(mTopActivity);
verifyOnActivityLaunchFinished(mTopActivity);
verifyNoMoreInteractions(mLaunchObserver);
@@ -231,8 +263,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
assertWithMessage("Record start source").that(info.sourceType)
.isEqualTo(SourceInfo.TYPE_LAUNCHER);
assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10);
- assertWithMessage("Hot launch").that(info.getLaunchState())
- .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
verifyOnActivityLaunchFinished(mTopActivity);
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 bb526695edfb..53ade0ea64be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -32,7 +30,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -125,61 +123,65 @@ import org.mockito.invocation.InvocationOnMock;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityRecordTests extends WindowTestsBase {
- private Task mStack;
- private Task mTask;
- private ActivityRecord mActivity;
@Before
public void setUp() throws Exception {
- mTask = new TaskBuilder(mSupervisor)
- .setCreateParentTask(true).setCreateActivity(true).build();
- mStack = mTask.getRootTask();
- mActivity = mTask.getTopNonFinishingActivity();
-
setBooted(mAtm);
}
@Test
public void testStackCleanupOnClearingTask() {
- mActivity.onParentChanged(null /*newParent*/, mActivity.getTask());
- verify(mStack, times(1)).cleanUpActivityReferences(any());
+ final ActivityRecord activity = createActivityWith2LevelTask();
+ final Task task = activity.getTask();
+ final Task rootTask = activity.getRootTask();
+ activity.onParentChanged(null /*newParent*/, task);
+ verify(rootTask, times(1)).cleanUpActivityReferences(any());
}
@Test
public void testStackCleanupOnActivityRemoval() {
- mTask.removeChild(mActivity);
- verify(mStack, times(1)).cleanUpActivityReferences(any());
+ final ActivityRecord activity = createActivityWith2LevelTask();
+ final Task task = activity.getTask();
+ final Task rootTask = activity.getRootTask();
+ task.removeChild(activity);
+ verify(rootTask, times(1)).cleanUpActivityReferences(any());
}
@Test
public void testStackCleanupOnTaskRemoval() {
- mStack.removeChild(mTask, null /*reason*/);
- // Stack should be gone on task removal.
- assertNull(mAtm.mRootWindowContainer.getStack(mStack.mTaskId));
+ final ActivityRecord activity = createActivityWith2LevelTask();
+ final Task task = activity.getTask();
+ final Task rootTask = activity.getRootTask();
+ rootTask.removeChild(task, null /*reason*/);
+ // parentTask should be gone on task removal.
+ assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId));
}
@Test
public void testRemoveChildWithOverlayActivity() {
- final ActivityRecord overlayActivity =
- new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task).build();
overlayActivity.setTaskOverlay(true);
- final ActivityRecord overlayActivity2 =
- new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord overlayActivity2 = new ActivityBuilder(mAtm).setTask(task).build();
overlayActivity2.setTaskOverlay(true);
- mTask.removeChild(overlayActivity2, "test");
+ task.removeChild(overlayActivity2, "test");
verify(mSupervisor, never()).removeTask(any(), anyBoolean(), anyBoolean(), any());
}
@Test
public void testNoCleanupMovingActivityInSameStack() {
- final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
- mActivity.reparent(newTask, 0, null /*reason*/);
- verify(mStack, times(0)).cleanUpActivityReferences(any());
+ final ActivityRecord activity = createActivityWith2LevelTask();
+ final Task rootTask = activity.getRootTask();
+ final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build();
+ activity.reparent(newTask, 0, null /*reason*/);
+ verify(rootTask, times(0)).cleanUpActivityReferences(any());
}
@Test
public void testPausingWhenVisibleFromStopped() throws Exception {
+ final ActivityRecord activity = createActivityWithTask();
final MutableBoolean pauseFound = new MutableBoolean(false);
doAnswer((InvocationOnMock invocationOnMock) -> {
final ClientTransaction transaction = invocationOnMock.getArgument(0);
@@ -187,49 +189,50 @@ public class ActivityRecordTests extends WindowTestsBase {
pauseFound.value = true;
}
return null;
- }).when(mActivity.app.getThread()).scheduleTransaction(any());
+ }).when(activity.app.getThread()).scheduleTransaction(any());
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
+ activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
// The activity is in the focused stack so it should be resumed.
- mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(RESUMED));
+ activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+ assertTrue(activity.isState(RESUMED));
assertFalse(pauseFound.value);
// Make the activity non focusable
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
- doReturn(false).when(mActivity).isFocusable();
+ activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
+ doReturn(false).when(activity).isFocusable();
// If the activity is not focusable, it should move to paused.
- mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(PAUSING));
+ activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+ assertTrue(activity.isState(PAUSING));
assertTrue(pauseFound.value);
// Make sure that the state does not change for current non-stopping states.
- mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
- doReturn(true).when(mActivity).isFocusable();
+ activity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
+ doReturn(true).when(activity).isFocusable();
- mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+ activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(INITIALIZING));
+ assertTrue(activity.isState(INITIALIZING));
// Make sure the state does not change if we are not the current top activity.
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
+ activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind");
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- mStack.mTranslucentActivityWaiting = topActivity;
- mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(STARTED));
+ final Task task = activity.getTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ task.mTranslucentActivityWaiting = topActivity;
+ activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+ assertTrue(activity.isState(STARTED));
- mStack.mTranslucentActivityWaiting = null;
+ task.mTranslucentActivityWaiting = null;
topActivity.setOccludesParent(false);
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
- mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(STARTED));
+ activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
+ activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+ assertTrue(activity.isState(STARTED));
}
- private void ensureActivityConfiguration() {
- mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ private void ensureActivityConfiguration(ActivityRecord activity) {
+ activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
}
@Test
@@ -245,136 +248,145 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testsApplyOptionsLocked() {
+ final ActivityRecord activity = createActivityWithTask();
ActivityOptions activityOptions = ActivityOptions.makeBasic();
// Set and apply options for ActivityRecord. Pending options should be cleared
- mActivity.updateOptionsLocked(activityOptions);
- mActivity.applyOptionsLocked();
- assertNull(mActivity.pendingOptions);
+ activity.updateOptionsLocked(activityOptions);
+ activity.applyOptionsLocked();
+ assertNull(activity.pendingOptions);
// Set options for two ActivityRecords in same Task. Apply one ActivityRecord options.
// Pending options should be cleared for both ActivityRecords
- ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(mTask).build();
+ ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
activity2.updateOptionsLocked(activityOptions);
- mActivity.updateOptionsLocked(activityOptions);
- mActivity.applyOptionsLocked();
- assertNull(mActivity.pendingOptions);
+ activity.updateOptionsLocked(activityOptions);
+ activity.applyOptionsLocked();
+ assertNull(activity.pendingOptions);
assertNull(activity2.pendingOptions);
// Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
// Pending options should be cleared for only ActivityRecord that was applied
- Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
- activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
+ activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity2.updateOptionsLocked(activityOptions);
- mActivity.updateOptionsLocked(activityOptions);
- mActivity.applyOptionsLocked();
- assertNull(mActivity.pendingOptions);
+ activity.updateOptionsLocked(activityOptions);
+ activity.applyOptionsLocked();
+ assertNull(activity.pendingOptions);
assertNotNull(activity2.pendingOptions);
}
@Test
public void testNewOverrideConfigurationIncrementsSeq() {
+ final ActivityRecord activity = createActivityWithTask();
final Configuration newConfig = new Configuration();
- final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
- mActivity.onRequestedOverrideConfigurationChanged(newConfig);
- assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+ final int prevSeq = activity.getMergedOverrideConfiguration().seq;
+ activity.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq);
}
@Test
public void testNewParentConfigurationIncrementsSeq() {
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
final Configuration newConfig = new Configuration(
- mTask.getRequestedOverrideConfiguration());
+ task.getRequestedOverrideConfiguration());
newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
- final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
- mTask.onRequestedOverrideConfigurationChanged(newConfig);
- assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq);
+ final int prevSeq = activity.getMergedOverrideConfiguration().seq;
+ task.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq);
}
@Test
public void testSetsRelaunchReason_NotDragResizing() {
- mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.RESUMED, "Testing");
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
- mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- mActivity.getConfiguration()));
+ task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+ activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+ activity.getConfiguration()));
- mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
- final Configuration newConfig = new Configuration(mTask.getConfiguration());
+ activity.info.configChanges &= ~CONFIG_ORIENTATION;
+ final Configuration newConfig = new Configuration(task.getConfiguration());
newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
? ORIENTATION_LANDSCAPE
: ORIENTATION_PORTRAIT;
- mTask.onRequestedOverrideConfigurationChanged(newConfig);
+ task.onRequestedOverrideConfigurationChanged(newConfig);
- mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+ activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
- ensureActivityConfiguration();
+ ensureActivityConfiguration(activity);
assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE,
- mActivity.mRelaunchReason);
+ activity.mRelaunchReason);
}
@Test
public void testSetsRelaunchReason_DragResizing() {
- mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.RESUMED, "Testing");
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
- mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- mActivity.getConfiguration()));
+ task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+ activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+ activity.getConfiguration()));
- mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
- final Configuration newConfig = new Configuration(mTask.getConfiguration());
+ activity.info.configChanges &= ~CONFIG_ORIENTATION;
+ final Configuration newConfig = new Configuration(task.getConfiguration());
newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
? ORIENTATION_LANDSCAPE
: ORIENTATION_PORTRAIT;
- mTask.onRequestedOverrideConfigurationChanged(newConfig);
+ task.onRequestedOverrideConfigurationChanged(newConfig);
- doReturn(true).when(mTask).isDragResizing();
+ doReturn(true).when(task).isDragResizing();
- mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+ activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
- ensureActivityConfiguration();
+ ensureActivityConfiguration(activity);
assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE,
- mActivity.mRelaunchReason);
+ activity.mRelaunchReason);
}
@Test
public void testSetsRelaunchReason_NonResizeConfigChanges() {
- mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.RESUMED, "Testing");
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
- mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- mActivity.getConfiguration()));
+ task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
+ activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+ activity.getConfiguration()));
- mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
- final Configuration newConfig = new Configuration(mTask.getConfiguration());
+ activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
+ final Configuration newConfig = new Configuration(task.getConfiguration());
newConfig.fontScale = 5;
- mTask.onRequestedOverrideConfigurationChanged(newConfig);
+ task.onRequestedOverrideConfigurationChanged(newConfig);
- mActivity.mRelaunchReason =
+ activity.mRelaunchReason =
ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
- ensureActivityConfiguration();
+ ensureActivityConfiguration(activity);
assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE,
- mActivity.mRelaunchReason);
+ activity.mRelaunchReason);
}
@Test
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
- mActivity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- mActivity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(Task.ActivityState.RESUMED, "Testing");
- mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- mActivity.getConfiguration()));
+ activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+ activity.getConfiguration()));
- final Configuration newConfig = new Configuration(mActivity.getConfiguration());
+ final Configuration newConfig = new Configuration(activity.getConfiguration());
final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
if (newConfig.orientation == ORIENTATION_PORTRAIT) {
@@ -388,7 +400,7 @@ public class ActivityRecordTests extends WindowTestsBase {
}
// Mimic the behavior that display doesn't handle app's requested orientation.
- final DisplayContent dc = mTask.getDisplayContent();
+ final DisplayContent dc = activity.getTask().getDisplayContent();
doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
@@ -404,24 +416,26 @@ public class ActivityRecordTests extends WindowTestsBase {
throw new IllegalStateException("Orientation in new config should be either"
+ "landscape or portrait.");
}
- mActivity.setRequestedOrientation(requestedOrientation);
+ activity.setRequestedOrientation(requestedOrientation);
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(newConfig);
- verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()),
- eq(mActivity.appToken), eq(expected));
+ verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()),
+ eq(activity.appToken), eq(expected));
}
@Test
public void ignoreRequestedOrientationInFreeformWindows() {
- mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_FREEFORM);
final Rect stableRect = new Rect();
- mStack.mDisplayContent.getStableRect(stableRect);
+ task.mDisplayContent.getStableRect(stableRect);
// Carve out non-decor insets from stableRect
final Rect insets = new Rect();
- final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
- final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
+ final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo();
+ final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy();
policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
displayInfo.logicalHeight, displayInfo.displayCutout, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -440,27 +454,30 @@ public class ActivityRecordTests extends WindowTestsBase {
bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2;
bounds.right = bounds.left + newWidth;
}
- mTask.setBounds(bounds);
+ task.setBounds(bounds);
// Requests orientation that's different from its bounds.
- mActivity.setRequestedOrientation(
+ activity.setRequestedOrientation(
isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
// Asserts it has orientation derived from bounds.
assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
- mActivity.getConfiguration().orientation);
+ activity.getConfiguration().orientation);
}
@Test
public void ignoreRequestedOrientationInSplitWindows() {
- mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ final ActivityRecord activity = createActivityWith2LevelTask();
+ final Task task = activity.getTask();
+ final Task rootTask = activity.getRootTask();
+ rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final Rect stableRect = new Rect();
- mStack.mDisplayContent.getStableRect(stableRect);
+ rootTask.mDisplayContent.getStableRect(stableRect);
// Carve out non-decor insets from stableRect
final Rect insets = new Rect();
- final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
- final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
+ final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo();
+ final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
displayInfo.logicalHeight, displayInfo.displayCutout, insets);
policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -479,85 +496,91 @@ public class ActivityRecordTests extends WindowTestsBase {
bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2;
bounds.right = bounds.left + newWidth;
}
- mTask.setBounds(bounds);
+ task.setBounds(bounds);
// Requests orientation that's different from its bounds.
- mActivity.setRequestedOrientation(
+ activity.setRequestedOrientation(
isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
// Asserts it has orientation derived from bounds.
assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
- mActivity.getConfiguration().orientation);
+ activity.getConfiguration().orientation);
}
@Test
public void testShouldMakeActive_deferredResume() {
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
mSupervisor.beginDeferResume();
- assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+ assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
mSupervisor.endDeferResume();
- assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */));
+ assertEquals(true, activity.shouldMakeActive(null /* activeActivity */));
}
@Test
public void testShouldMakeActive_nonTopVisible() {
- ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
finishingActivity.finishing = true;
- ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+ ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
- assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */));
+ assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
}
@Test
public void testShouldResume_stackVisibility() {
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
- spyOn(mStack);
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
- doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
- assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
+ doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null);
- assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
+ doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+ assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null);
- assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */));
+ doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+ assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
}
@Test
public void testShouldResumeOrPauseWithResults() {
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
- spyOn(mStack);
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
- ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- mActivity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
+ ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
topActivity.finishing = true;
- doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null);
- assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */));
- assertEquals(false, mActivity.shouldPauseActivity(null /*activeActivity */));
+ doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
+ assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
}
@Test
public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
- mActivity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
.setLaunchTaskBehind(true)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
+ final Task task = activity.getTask();
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
doReturn(false).when(stack).isTranslucent(any());
- assertTrue(mStack.shouldBeVisible(null /* starting */));
+ assertTrue(task.shouldBeVisible(null /* starting */));
- mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- mActivity.getConfiguration()));
+ activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
+ activity.getConfiguration()));
- final Configuration newConfig = new Configuration(mActivity.getConfiguration());
+ final Configuration newConfig = new Configuration(activity.getConfiguration());
final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
if (newConfig.orientation == ORIENTATION_PORTRAIT) {
@@ -570,15 +593,15 @@ public class ActivityRecordTests extends WindowTestsBase {
newConfig.screenHeightDp = longSide;
}
- mTask.onConfigurationChanged(newConfig);
+ task.onConfigurationChanged(newConfig);
- mActivity.ensureActivityConfiguration(0 /* globalChanges */,
+ activity.ensureActivityConfiguration(0 /* globalChanges */,
false /* preserveWindow */, true /* ignoreStopState */);
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(newConfig);
verify(mAtm.getLifecycleManager()).scheduleTransaction(
- eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected));
+ eq(activity.app.getThread()), eq(activity.appToken), eq(expected));
} finally {
stack.getDisplayArea().removeChild(stack);
}
@@ -586,16 +609,18 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testShouldStartWhenMakeClientActive() {
- ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
topActivity.setOccludesParent(false);
- mActivity.setState(Task.ActivityState.STOPPED, "Testing");
- mActivity.setVisibility(true);
- mActivity.makeActiveIfNeeded(null /* activeActivity */);
- assertEquals(STARTED, mActivity.getState());
+ activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setVisibility(true);
+ activity.makeActiveIfNeeded(null /* activeActivity */);
+ assertEquals(STARTED, activity.getState());
}
@Test
public void testTakeOptions() {
+ final ActivityRecord activity = createActivityWithTask();
ActivityOptions opts = ActivityOptions.makeRemoteAnimation(
new RemoteAnimationAdapter(new Stub() {
@@ -611,13 +636,13 @@ public class ActivityRecordTests extends WindowTestsBase {
}
}, 0, 0));
- mActivity.updateOptionsLocked(opts);
- assertNotNull(mActivity.takeOptionsLocked(true /* fromClient */));
- assertNotNull(mActivity.pendingOptions);
+ activity.updateOptionsLocked(opts);
+ assertNotNull(activity.takeOptionsLocked(true /* fromClient */));
+ assertNotNull(activity.pendingOptions);
- mActivity.updateOptionsLocked(ActivityOptions.makeBasic());
- assertNotNull(mActivity.takeOptionsLocked(false /* fromClient */));
- assertNull(mActivity.pendingOptions);
+ activity.updateOptionsLocked(ActivityOptions.makeBasic());
+ assertNotNull(activity.takeOptionsLocked(false /* fromClient */));
+ assertNull(activity.pendingOptions);
}
@Test
@@ -626,7 +651,7 @@ public class ActivityRecordTests extends WindowTestsBase {
Resources.getSystem().getString(R.string.config_chooserActivity));
ActivityRecord chooserActivity = new ActivityBuilder(mAtm).setComponent(
chooserComponent).build();
- assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue();
+ assertThat(chooserActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue();
}
/**
@@ -635,49 +660,52 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testHasSavedState() {
- assertTrue(mActivity.hasSavedState());
+ final ActivityRecord activity = createActivityWithTask();
+ assertTrue(activity.hasSavedState());
- ActivityRecord.activityResumedLocked(mActivity.appToken);
- assertFalse(mActivity.hasSavedState());
- assertNull(mActivity.getSavedState());
+ ActivityRecord.activityResumedLocked(activity.appToken);
+ assertFalse(activity.hasSavedState());
+ assertNull(activity.getSavedState());
}
/** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */
@Test
public void testUpdateSavedState() {
- mActivity.setSavedState(null /* savedState */);
- assertFalse(mActivity.hasSavedState());
- assertNull(mActivity.getSavedState());
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setSavedState(null /* savedState */);
+ assertFalse(activity.hasSavedState());
+ assertNull(activity.getSavedState());
final Bundle savedState = new Bundle();
savedState.putString("test", "string");
- mActivity.setSavedState(savedState);
- assertTrue(mActivity.hasSavedState());
- assertEquals(savedState, mActivity.getSavedState());
+ activity.setSavedState(savedState);
+ assertTrue(activity.hasSavedState());
+ assertEquals(savedState, activity.getSavedState());
}
/** Verify the correct updates of saved state when activity client reports stop. */
@Test
public void testUpdateSavedState_activityStopped() {
+ final ActivityRecord activity = createActivityWithTask();
final Bundle savedState = new Bundle();
savedState.putString("test", "string");
final PersistableBundle persistentSavedState = new PersistableBundle();
persistentSavedState.putString("persist", "string");
// Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored.
- mActivity.setState(STOPPING, "test");
- mActivity.activityStopped(savedState, persistentSavedState, "desc");
- assertTrue(mActivity.hasSavedState());
- assertEquals(savedState, mActivity.getSavedState());
- assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+ activity.setState(STOPPING, "test");
+ activity.activityStopped(savedState, persistentSavedState, "desc");
+ assertTrue(activity.hasSavedState());
+ assertEquals(savedState, activity.getSavedState());
+ assertEquals(persistentSavedState, activity.getPersistentSavedState());
// Sending 'null' for saved state can only happen due to timeout, so previously stored saved
// states should not be overridden.
- mActivity.setState(STOPPING, "test");
- mActivity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc");
- assertTrue(mActivity.hasSavedState());
- assertEquals(savedState, mActivity.getSavedState());
- assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+ activity.setState(STOPPING, "test");
+ activity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc");
+ assertTrue(activity.hasSavedState());
+ assertEquals(savedState, activity.getSavedState());
+ assertEquals(persistentSavedState, activity.getPersistentSavedState());
}
/**
@@ -686,19 +714,20 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_cancelled() {
+ final ActivityRecord activity = createActivityWithTask();
// Mark activity as finishing
- mActivity.finishing = true;
+ activity.finishing = true;
assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- assertTrue(mActivity.finishing);
- assertTrue(mActivity.isInStackLocked());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ assertTrue(activity.finishing);
+ assertTrue(activity.isInStackLocked());
// Remove activity from task
- mActivity.finishing = false;
- mActivity.onParentChanged(null /*newParent*/, mActivity.getTask());
+ activity.finishing = false;
+ activity.onParentChanged(null /*newParent*/, activity.getTask());
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- assertFalse(mActivity.finishing);
+ activity.finishIfPossible("test", false /* oomAdj */));
+ assertFalse(activity.finishing);
}
/**
@@ -707,20 +736,21 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_requested() {
- mActivity.finishing = false;
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = false;
assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- assertTrue(mActivity.finishing);
- assertTrue(mActivity.isInStackLocked());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ assertTrue(activity.finishing);
+ assertTrue(activity.isInStackLocked());
// First request to finish activity must schedule a "destroy" request to the client.
// Activity must be removed from history after the client reports back or after timeout.
- mActivity.finishing = false;
- mActivity.setState(STOPPED, "test");
+ activity.finishing = false;
+ activity.setState(STOPPED, "test");
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- assertTrue(mActivity.finishing);
- assertTrue(mActivity.isInStackLocked());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ assertTrue(activity.finishing);
+ assertTrue(activity.isInStackLocked());
}
/**
@@ -728,26 +758,28 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_removed() {
+ final ActivityRecord activity = createActivityWithTask();
// Prepare the activity record to be ready for immediate removal. It should be invisible and
// have no process. Otherwise, request to finish it will send a message to client first.
- mActivity.setState(STOPPED, "test");
- mActivity.mVisibleRequested = false;
- mActivity.nowVisible = false;
+ activity.setState(STOPPED, "test");
+ activity.mVisibleRequested = false;
+ activity.nowVisible = false;
// Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() -
// this will cause NPE when updating task's process.
- mActivity.app = null;
+ activity.app = null;
// Put a visible activity on top, so the finishing activity doesn't have to wait until the
// next activity reports idle to destroy it.
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
topActivity.setState(RESUMED, "test");
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- assertTrue(mActivity.finishing);
- assertFalse(mActivity.isInStackLocked());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ assertTrue(activity.finishing);
+ assertFalse(activity.isInStackLocked());
}
/**
@@ -756,24 +788,26 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_adjustStackOrder() {
- // Prepare the stacks with order (top to bottom): mStack, stack1, stack2.
- final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
- mStack.moveToFront("test");
- // The stack2 is needed here for moving back to simulate the
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ // Prepare the tasks with order (top to bottom): task, task1, task2.
+ final Task task1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ task.moveToFront("test");
+ // The task2 is needed here for moving back to simulate the
// {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so
// {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible
- // stacks. Then when mActivity is finishing, its stack will be invisible (no running
- // activities in the stack) that is the key condition to verify.
- final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
- stack2.moveToBack("test", stack2.getBottomMostTask());
+ // tasks. Then when mActivity is finishing, its task will be invisible (no running
+ // activities in the task) that is the key condition to verify.
+ final Task task2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ task2.moveToBack("test", task2.getBottomMostTask());
- assertTrue(mStack.isTopStackInDisplayArea());
+ assertTrue(task.isTopStackInDisplayArea());
- mActivity.setState(RESUMED, "test");
- mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
+ activity.setState(RESUMED, "test");
+ activity.finishIfPossible(0 /* resultCode */, null /* resultData */,
null /* resultGrants */, "test", false /* oomAdj */);
- assertTrue(stack1.isTopStackInDisplayArea());
+ assertTrue(task1.isTopStackInDisplayArea());
}
/**
@@ -783,22 +817,25 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testFinishActivityIfPossible_adjustStackOrderOrganizedRoot() {
// Make mStack be a the root task that created by task organizer
- mStack.mCreatedByOrganizer = true;
+ final Task rootableTask = new TaskBuilder(mSupervisor)
+ .setCreateParentTask(true).setCreateActivity(true).build();
+ final Task rootTask = rootableTask.getRootTask();
+ rootTask.mCreatedByOrganizer = true;
- // Have two tasks (topRootableTask and mTask) as the children of mStack.
- ActivityRecord topActivity = new ActivityBuilder(mActivity.mAtmService)
+ // Have two tasks (topRootableTask and rootableTask) as the children of rootTask.
+ ActivityRecord topActivity = new ActivityBuilder(mAtm)
.setCreateTask(true)
- .setParentTask(mStack)
+ .setParentTask(rootTask)
.build();
Task topRootableTask = topActivity.getTask();
topRootableTask.moveToFront("test");
- assertTrue(mStack.isTopStackInDisplayArea());
+ assertTrue(rootTask.isTopStackInDisplayArea());
// Finish top activity and verify the next focusable rootable task has adjusted to top.
topActivity.setState(RESUMED, "test");
topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */,
null /* resultGrants */, "test", false /* oomAdj */);
- assertEquals(mTask, mStack.getTopMostTask());
+ assertEquals(rootableTask, rootTask.getTopMostTask());
}
/**
@@ -808,6 +845,8 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_PreferredTopStackChanged() {
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
final ActivityRecord topActivityOnNonTopDisplay =
createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
Task topRootableTask = topActivityOnNonTopDisplay.getRootTask();
@@ -830,8 +869,8 @@ public class ActivityRecordTests extends WindowTestsBase {
topActivityOnNonTopDisplay.setState(RESUMED, "test");
topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */,
null /* resultGrants */, "test", false /* oomAdj */);
- assertEquals(mTask, mStack.getTopMostTask());
- assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack);
+ assertEquals(task, task.getTopMostTask());
+ assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack);
}
/**
@@ -839,15 +878,14 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_resumedStartsPausing() {
- mActivity.finishing = false;
- mActivity.setState(RESUMED, "test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = false;
+ activity.setState(RESUMED, "test");
assertEquals("Currently resumed activity must be paused before removal",
- FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
- assertEquals(PAUSING, mActivity.getState());
- verify(mActivity).setVisibility(eq(false));
- verify(mActivity.mDisplayContent)
- .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
- eq(false) /* alwaysKeepCurrent */);
+ FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */));
+ assertEquals(PAUSING, activity.getState());
+ verify(activity).setVisibility(eq(false));
+ verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
}
/**
@@ -855,14 +893,15 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
+ final ActivityRecord activity = createActivityWithTask();
final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
for (ActivityState state : states) {
- mActivity.finishing = false;
- mActivity.setState(state, "test");
- reset(mActivity);
+ activity.finishing = false;
+ activity.setState(state, "test");
+ reset(activity);
assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- verify(mActivity).completeFinishing(anyString());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ verify(activity).completeFinishing(anyString());
}
}
@@ -871,11 +910,12 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_pausing() {
- mActivity.finishing = false;
- mActivity.setState(PAUSING, "test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = false;
+ activity.setState(PAUSING, "test");
assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
- mActivity.finishIfPossible("test", false /* oomAdj */));
- verify(mActivity, never()).completeFinishing(anyString());
+ activity.finishIfPossible("test", false /* oomAdj */));
+ verify(activity, never()).completeFinishing(anyString());
}
/**
@@ -884,16 +924,16 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
- mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
- mActivity.setState(RESUMED, "test");
- mActivity.finishIfPossible("test", false /* oomAdj */);
+ final ActivityRecord activity = createActivityWithTask();
+ clearInvocations(activity.mDisplayContent);
+ activity.finishing = false;
+ activity.mVisibleRequested = true;
+ activity.setState(RESUMED, "test");
+ activity.finishIfPossible("test", false /* oomAdj */);
- verify(mActivity).setVisibility(eq(false));
- verify(mActivity.mDisplayContent)
- .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
- eq(false) /* alwaysKeepCurrent */);
- verify(mActivity.mDisplayContent, never()).executeAppTransition();
+ verify(activity).setVisibility(eq(false));
+ verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
+ verify(activity.mDisplayContent, never()).executeAppTransition();
}
/**
@@ -901,16 +941,16 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
- mActivity.finishing = false;
- mActivity.mVisibleRequested = true;
- mActivity.setState(PAUSED, "test");
- mActivity.finishIfPossible("test", false /* oomAdj */);
+ final ActivityRecord activity = createActivityWithTask();
+ clearInvocations(activity.mDisplayContent);
+ activity.finishing = false;
+ activity.mVisibleRequested = true;
+ activity.setState(PAUSED, "test");
+ activity.finishIfPossible("test", false /* oomAdj */);
- verify(mActivity, atLeast(1)).setVisibility(eq(false));
- verify(mActivity.mDisplayContent)
- .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
- eq(false) /* alwaysKeepCurrent */);
- verify(mActivity.mDisplayContent).executeAppTransition();
+ verify(activity, atLeast(1)).setVisibility(eq(false));
+ verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
+ verify(activity.mDisplayContent).executeAppTransition();
}
/**
@@ -918,17 +958,16 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
+ final ActivityRecord activity = createActivityWithTask();
// Put an activity on top of test activity to make it invisible and prevent us from
// accidentally resuming the topmost one again.
new ActivityBuilder(mAtm).build();
- mActivity.mVisibleRequested = false;
- mActivity.setState(STOPPED, "test");
+ activity.mVisibleRequested = false;
+ activity.setState(STOPPED, "test");
- mActivity.finishIfPossible("test", false /* oomAdj */);
+ activity.finishIfPossible("test", false /* oomAdj */);
- verify(mActivity.mDisplayContent, never())
- .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
- eq(false) /* alwaysKeepCurrent */);
+ verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE));
}
/**
@@ -936,8 +975,9 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test(expected = IllegalArgumentException.class)
public void testCompleteFinishing_failNotFinishing() {
- mActivity.finishing = false;
- mActivity.completeFinishing("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = false;
+ activity.completeFinishing("test");
}
/**
@@ -945,8 +985,9 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test(expected = IllegalArgumentException.class)
public void testCompleteFinishing_failResumed() {
- mActivity.setState(RESUMED, "test");
- mActivity.completeFinishing("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(RESUMED, "test");
+ activity.completeFinishing("test");
}
/**
@@ -955,13 +996,14 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_pausing() {
- mActivity.setState(PAUSING, "test");
- mActivity.finishing = true;
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(PAUSING, "test");
+ activity.finishing = true;
assertEquals("Activity must not be removed immediately - waiting for paused",
- mActivity, mActivity.completeFinishing("test"));
- assertEquals(PAUSING, mActivity.getState());
- verify(mActivity, never()).destroyIfPossible(anyString());
+ activity, activity.completeFinishing("test"));
+ assertEquals(PAUSING, activity.getState());
+ verify(activity, never()).destroyIfPossible(anyString());
}
/**
@@ -973,7 +1015,9 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_keepStateOfNextInvisible() {
- final ActivityRecord currentTop = mActivity;
+ final ActivityRecord currentTop = createActivityWithTask();
+ final Task task = currentTop.getTask();
+
currentTop.mVisibleRequested = currentTop.nowVisible = true;
// Simulates that {@code currentTop} starts an existing activity from background (so its
@@ -982,7 +1026,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity();
nextTop.setState(STOPPED, "test");
- mStack.mPausingActivity = currentTop;
+ task.mPausingActivity = currentTop;
currentTop.finishing = true;
currentTop.setState(PAUSED, "test");
currentTop.completeFinishing("completePauseLocked");
@@ -999,16 +1043,18 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_waitForNextVisible() {
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as not visible, so that we will wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
- mActivity.nowVisible = false;
- mActivity.setState(STOPPED, "test");
+ activity.mVisibleRequested = false;
+ activity.nowVisible = false;
+ activity.setState(STOPPED, "test");
assertEquals("Activity must not be removed immediately - waiting for next visible",
topActivity, topActivity.completeFinishing("test"));
@@ -1025,23 +1071,24 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_noWaitForNextVisible_sleeping() {
+ final ActivityRecord activity = createActivityWithTask();
// Create a top activity on a new task
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord topActivity = createActivityWithTask();
mDisplayContent.setIsSleeping(true);
- doReturn(true).when(mActivity).shouldBeVisible();
+ doReturn(true).when(activity).shouldBeVisible();
topActivity.mVisibleRequested = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(STOPPED, "true");
// Mark the activity behind (on a separate task) as not visible
- mActivity.mVisibleRequested = false;
- mActivity.nowVisible = false;
- mActivity.setState(STOPPED, "test");
+ activity.mVisibleRequested = false;
+ activity.nowVisible = false;
+ activity.setState(STOPPED, "test");
- clearInvocations(mActivity);
+ clearInvocations(activity);
topActivity.completeFinishing("test");
- verify(mActivity).setState(eq(RESUMED), any());
+ verify(activity).setState(eq(RESUMED), any());
verify(topActivity).destroyIfPossible(anyString());
}
@@ -1050,16 +1097,18 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(STOPPED, "true");
// Mark the bottom activity as not visible, so that we would wait for it before removing
// the top one.
- mActivity.mVisibleRequested = false;
- mActivity.nowVisible = false;
- mActivity.setState(STOPPED, "test");
+ activity.mVisibleRequested = false;
+ activity.nowVisible = false;
+ activity.setState(STOPPED, "test");
topActivity.completeFinishing("test");
@@ -1072,15 +1121,17 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_waitForIdle() {
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
- mActivity.nowVisible = true;
- mActivity.setState(RESUMED, "test");
+ activity.mVisibleRequested = true;
+ activity.nowVisible = true;
+ activity.setState(RESUMED, "test");
topActivity.completeFinishing("test");
@@ -1093,15 +1144,17 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_noWaitForNextVisible_stopped() {
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = false;
topActivity.nowVisible = false;
topActivity.finishing = true;
topActivity.setState(STOPPED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
- mActivity.nowVisible = true;
- mActivity.setState(RESUMED, "test");
+ activity.mVisibleRequested = true;
+ activity.nowVisible = true;
+ activity.setState(RESUMED, "test");
topActivity.completeFinishing("test");
@@ -1114,15 +1167,17 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(activity.getTask()).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
topActivity.finishing = true;
topActivity.setState(PAUSED, "true");
// Mark the bottom activity as already visible, so that there is no need to wait for it.
- mActivity.mVisibleRequested = true;
- mActivity.nowVisible = true;
- mActivity.setState(RESUMED, "test");
+ activity.mVisibleRequested = true;
+ activity.nowVisible = true;
+ activity.setState(RESUMED, "test");
// Add another stack to become focused and make the activity there visible. This way it
// simulates finishing in non-focused stack in split-screen.
@@ -1144,10 +1199,12 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_showWhenLocked() {
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
// Make keyguard locked and set the top activity show-when-locked.
- KeyguardController keyguardController = mActivity.mTaskSupervisor.getKeyguardController();
+ KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
doReturn(true).when(keyguardController).isKeyguardLocked();
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
topActivity.setState(RESUMED, "true");
@@ -1158,7 +1215,7 @@ public class ActivityRecordTests extends WindowTestsBase {
topActivity.setShowWhenLocked(true);
// Verify the stack-top activity is occluded keyguard.
- assertEquals(topActivity, mStack.topRunningActivity());
+ assertEquals(topActivity, task.topRunningActivity());
assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
// Finish the top activity
@@ -1167,7 +1224,7 @@ public class ActivityRecordTests extends WindowTestsBase {
topActivity.completeFinishing("test");
// Verify new top activity does not occlude keyguard.
- assertEquals(mActivity, mStack.topRunningActivity());
+ assertEquals(activity, task.topRunningActivity());
assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
}
@@ -1177,18 +1234,19 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_ensureActivitiesVisible() {
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
firstActivity.mVisibleRequested = false;
firstActivity.nowVisible = false;
firstActivity.setState(STOPPED, "true");
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
secondActivity.mVisibleRequested = true;
secondActivity.nowVisible = true;
secondActivity.setState(PAUSED, "true");
- final ActivityRecord translucentActivity =
- new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
translucentActivity.mVisibleRequested = true;
translucentActivity.nowVisible = true;
translucentActivity.setState(RESUMED, "true");
@@ -1216,13 +1274,13 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyIfPossible() {
+ final ActivityRecord activity = createActivityWithTask();
doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
- spyOn(mStack);
- mActivity.destroyIfPossible("test");
+ activity.destroyIfPossible("test");
- assertEquals(DESTROYING, mActivity.getState());
- assertTrue(mActivity.finishing);
- verify(mActivity).destroyImmediately(anyString());
+ assertEquals(DESTROYING, activity.getState());
+ assertTrue(activity.finishing);
+ verify(activity).destroyImmediately(anyString());
}
/**
@@ -1232,23 +1290,23 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
+ final ActivityRecord activity = createActivityWithTask();
// Empty the home stack.
- final Task homeStack = mActivity.getDisplayArea().getRootHomeTask();
+ final Task homeStack = activity.getDisplayArea().getRootHomeTask();
homeStack.forAllLeafTasks((t) -> {
homeStack.removeChild(t, "test");
}, true /* traverseTopToBottom */);
- mActivity.finishing = true;
+ activity.finishing = true;
doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
- spyOn(mStack);
// Try to destroy the last activity above the home stack.
- mActivity.destroyIfPossible("test");
+ activity.destroyIfPossible("test");
// Verify that the activity was not actually destroyed, but waits for next one to come up
// instead.
- verify(mActivity, never()).destroyImmediately(anyString());
- assertEquals(FINISHING, mActivity.getState());
- assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity));
+ verify(activity, never()).destroyImmediately(anyString());
+ assertEquals(FINISHING, activity.getState());
+ assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity));
}
/**
@@ -1258,22 +1316,23 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
+ final ActivityRecord activity = createActivityWithTask();
// Empty the home root task.
- final Task homeRootTask = mActivity.getDisplayArea().getRootHomeTask();
+ final Task homeRootTask = activity.getDisplayArea().getRootHomeTask();
homeRootTask.forAllLeafTasks((t) -> {
homeRootTask.removeChild(t, "test");
}, true /* traverseTopToBottom */);
- mActivity.finishing = true;
- mActivity.mVisibleRequested = true;
- spyOn(mStack);
+ activity.setState(STARTED, "test");
+ activity.finishing = true;
+ activity.mVisibleRequested = true;
// Try to finish the last activity above the home stack.
- mActivity.completeFinishing("test");
+ activity.completeFinishing("test");
// Verify that the activity is not destroyed immediately, but waits for next one to come up.
- verify(mActivity, never()).destroyImmediately(anyString());
- assertEquals(FINISHING, mActivity.getState());
- assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity));
+ verify(activity, never()).destroyImmediately(anyString());
+ assertEquals(FINISHING, activity.getState());
+ assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity));
}
/**
@@ -1282,10 +1341,11 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyImmediately_hadApp_finishing() {
- mActivity.finishing = true;
- mActivity.destroyImmediately("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = true;
+ activity.destroyImmediately("test");
- assertEquals(DESTROYING, mActivity.getState());
+ assertEquals(DESTROYING, activity.getState());
}
/**
@@ -1294,10 +1354,11 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyImmediately_hadApp_notFinishing() {
- mActivity.finishing = false;
- mActivity.destroyImmediately("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.finishing = false;
+ activity.destroyImmediately("test");
- assertEquals(DESTROYED, mActivity.getState());
+ assertEquals(DESTROYED, activity.getState());
}
/**
@@ -1306,14 +1367,15 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyImmediately_noApp_finishing() {
- mActivity.app = null;
- mActivity.finishing = true;
- final Task task = mActivity.getTask();
+ final ActivityRecord activity = createActivityWithTask();
+ activity.app = null;
+ activity.finishing = true;
+ final Task task = activity.getTask();
- mActivity.destroyImmediately("test");
+ activity.destroyImmediately("test");
- assertEquals(DESTROYED, mActivity.getState());
- assertNull(mActivity.getTask());
+ assertEquals(DESTROYED, activity.getState());
+ assertNull(activity.getTask());
assertEquals(0, task.getChildCount());
}
@@ -1323,14 +1385,15 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyImmediately_noApp_notFinishing() {
- mActivity.app = null;
- mActivity.finishing = false;
- final Task task = mActivity.getTask();
+ final ActivityRecord activity = createActivityWithTask();
+ activity.app = null;
+ activity.finishing = false;
+ final Task task = activity.getTask();
- mActivity.destroyImmediately("test");
+ activity.destroyImmediately("test");
- assertEquals(DESTROYED, mActivity.getState());
- assertEquals(task, mActivity.getTask());
+ assertEquals(DESTROYED, activity.getState());
+ assertEquals(task, activity.getTask());
assertEquals(1, task.getChildCount());
}
@@ -1339,11 +1402,12 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testSafelyDestroy_nonDestroyable() {
- doReturn(false).when(mActivity).isDestroyable();
+ final ActivityRecord activity = createActivityWithTask();
+ doReturn(false).when(activity).isDestroyable();
- mActivity.safelyDestroy("test");
+ activity.safelyDestroy("test");
- verify(mActivity, never()).destroyImmediately(anyString());
+ verify(activity, never()).destroyImmediately(anyString());
}
/**
@@ -1351,29 +1415,31 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testSafelyDestroy_destroyable() {
- doReturn(true).when(mActivity).isDestroyable();
+ final ActivityRecord activity = createActivityWithTask();
+ doReturn(true).when(activity).isDestroyable();
- mActivity.safelyDestroy("test");
+ activity.safelyDestroy("test");
- verify(mActivity).destroyImmediately(anyString());
+ verify(activity).destroyImmediately(anyString());
}
@Test
public void testRemoveFromHistory() {
- final Task stack = mActivity.getRootTask();
- final Task task = mActivity.getTask();
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final Task rootTask = activity.getRootTask();
+ final Task task = activity.getTask();
+ final WindowProcessController wpc = activity.app;
assertTrue(wpc.hasActivities());
- mActivity.removeFromHistory("test");
+ activity.removeFromHistory("test");
- assertEquals(DESTROYED, mActivity.getState());
- assertNull(mActivity.app);
- assertNull(mActivity.getTask());
+ assertEquals(DESTROYED, activity.getState());
+ assertNull(activity.app);
+ assertNull(activity.getTask());
assertFalse(wpc.hasActivities());
assertEquals(0, task.getChildCount());
assertEquals(task.getRootTask(), task);
- assertEquals(0, stack.getChildCount());
+ assertEquals(0, rootTask.getChildCount());
}
/**
@@ -1382,8 +1448,9 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test(expected = IllegalStateException.class)
public void testDestroyed_notDestroying() {
- mActivity.setState(STOPPED, "test");
- mActivity.destroyed("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(STOPPED, "test");
+ activity.destroyed("test");
}
/**
@@ -1391,10 +1458,11 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyed_destroying() {
- mActivity.setState(DESTROYING, "test");
- mActivity.destroyed("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(DESTROYING, "test");
+ activity.destroyed("test");
- verify(mActivity).removeFromHistory(anyString());
+ verify(activity).removeFromHistory(anyString());
}
/**
@@ -1402,15 +1470,17 @@ public class ActivityRecordTests extends WindowTestsBase {
*/
@Test
public void testDestroyed_destroyed() {
- mActivity.setState(DESTROYED, "test");
- mActivity.destroyed("test");
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(DESTROYED, "test");
+ activity.destroyed("test");
- verify(mActivity).removeFromHistory(anyString());
+ verify(activity).removeFromHistory(anyString());
}
@Test
public void testActivityOverridesProcessConfig() {
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
assertTrue(wpc.registeredForActivityConfigChanges());
assertFalse(wpc.registeredForDisplayConfigChanges());
@@ -1418,18 +1488,19 @@ public class ActivityRecordTests extends WindowTestsBase {
createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
assertTrue(wpc.registeredForActivityConfigChanges());
- assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ assertEquals(0, activity.getMergedOverrideConfiguration()
.diff(wpc.getRequestedOverrideConfiguration()));
- assertNotEquals(mActivity.getConfiguration(),
+ assertNotEquals(activity.getConfiguration(),
secondaryDisplayActivity.getConfiguration());
}
@Test
public void testActivityOverridesProcessConfig_TwoActivities() {
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
assertTrue(wpc.registeredForActivityConfigChanges());
- final Task firstTaskRecord = mActivity.getTask();
+ final Task firstTaskRecord = activity.getTask();
final ActivityRecord secondActivityRecord =
new ActivityBuilder(mAtm).setTask(firstTaskRecord).setUseProcess(wpc).build();
@@ -1440,11 +1511,12 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() {
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
assertTrue(wpc.registeredForActivityConfigChanges());
final ActivityRecord secondActivityRecord =
- new ActivityBuilder(mAtm).setTask(mTask).setUseProcess(wpc).build();
+ new ActivityBuilder(mAtm).setTask(activity.getTask()).setUseProcess(wpc).build();
assertTrue(wpc.registeredForActivityConfigChanges());
assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
@@ -1453,7 +1525,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() {
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
assertTrue(wpc.registeredForActivityConfigChanges());
final ActivityRecord secondActivityRecord =
@@ -1466,10 +1539,11 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testActivityOnCancelFixedRotationTransform() {
- final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation();
+ final ActivityRecord activity = createActivityWithTask();
+ final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation();
spyOn(displayRotation);
- final DisplayContent display = mActivity.mDisplayContent;
+ final DisplayContent display = activity.mDisplayContent;
final int originalRotation = display.getRotation();
// Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
@@ -1477,17 +1551,17 @@ public class ActivityRecordTests extends WindowTestsBase {
doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
- mActivity.setVisibility(true);
+ activity.setVisibility(true);
- display.rotateInDifferentOrientationIfNeeded(mActivity);
- display.setFixedRotationLaunchingAppUnchecked(mActivity);
+ display.rotateInDifferentOrientationIfNeeded(activity);
+ display.setFixedRotationLaunchingAppUnchecked(activity);
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
assertTrue(displayRotation.isRotatingSeamlessly());
// The launching rotated app should not be cleared when waiting for remote rotation.
display.continueUpdateOrientationForDiffOrienLaunchingApp();
- assertTrue(display.isFixedRotationLaunchingApp(mActivity));
+ assertTrue(display.isFixedRotationLaunchingApp(activity));
// Simulate the rotation has been updated to previous one, e.g. sensor updates before the
// remote rotation is completed.
@@ -1495,16 +1569,16 @@ public class ActivityRecordTests extends WindowTestsBase {
anyInt() /* orientation */, anyInt() /* lastRotation */);
display.updateOrientation();
- final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
- mActivity.finishFixedRotationTransform();
+ final DisplayInfo rotatedInfo = activity.getFixedRotationTransformDisplayInfo();
+ activity.finishFixedRotationTransform();
final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
assertNotNull(rotationAnim);
rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
// Because the display doesn't rotate, the rotated activity needs to cancel the fixed
// rotation. There should be a rotation animation to cover the change of activity.
- verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
- assertTrue(mActivity.isFreezingScreen());
+ verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation);
+ assertTrue(activity.isFreezingScreen());
assertFalse(displayRotation.isRotatingSeamlessly());
assertTrue(rotationAnim.isRotating());
@@ -1512,44 +1586,46 @@ public class ActivityRecordTests extends WindowTestsBase {
// the rotated activity should also be restored by clearing the transform.
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
doReturn(false).when(displayRotation).isWaitingForRemoteRotation();
- clearInvocations(mActivity);
- display.setFixedRotationLaunchingAppUnchecked(mActivity);
+ clearInvocations(activity);
+ display.setFixedRotationLaunchingAppUnchecked(activity);
display.sendNewConfiguration();
assertFalse(display.hasTopFixedRotationLaunchingApp());
- assertFalse(mActivity.hasFixedRotationTransform());
+ assertFalse(activity.hasFixedRotationTransform());
}
@Test
public void testIsSnapshotCompatible() {
+ final ActivityRecord activity = createActivityWithTask();
final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
- .setRotation(mActivity.getWindowConfiguration().getRotation())
+ .setRotation(activity.getWindowConfiguration().getRotation())
.build();
- assertTrue(mActivity.isSnapshotCompatible(snapshot));
+ assertTrue(activity.isSnapshotCompatible(snapshot));
- setRotatedScreenOrientationSilently(mActivity);
+ setRotatedScreenOrientationSilently(activity);
- assertFalse(mActivity.isSnapshotCompatible(snapshot));
+ assertFalse(activity.isSnapshotCompatible(snapshot));
}
@Test
public void testFixedRotationSnapshotStartingWindow() {
+ final ActivityRecord activity = createActivityWithTask();
// TaskSnapshotSurface requires a fullscreen opaque window.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
final TestWindowState w = new TestWindowState(
- mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
- mActivity.addWindow(w);
+ mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity);
+ activity.addWindow(w);
// Assume the activity is launching in different rotation, and there was an available
// snapshot accepted by {@link Activity#isSnapshotCompatible}.
final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
- .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4)
+ .setRotation((activity.getWindowConfiguration().getRotation() + 1) % 4)
.build();
- setRotatedScreenOrientationSilently(mActivity);
- mActivity.setVisible(false);
+ setRotatedScreenOrientationSilently(activity);
+ activity.setVisible(false);
final IWindowSession session = WindowManagerGlobal.getWindowSession();
spyOn(session);
@@ -1561,7 +1637,7 @@ public class ActivityRecordTests extends WindowTestsBase {
any() /* requestedVisibility */, any() /* outFrame */,
any() /* outDisplayCutout */, any() /* outInputChannel */,
any() /* outInsetsState */, any() /* outActiveControls */);
- TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot);
+ TaskSnapshotSurface.create(mAtm.mWindowManager, activity, snapshot);
} catch (RemoteException ignored) {
} finally {
reset(session);
@@ -1570,8 +1646,8 @@ public class ActivityRecordTests extends WindowTestsBase {
// Because the rotation of snapshot and the corresponding top activity are different, fixed
// rotation should be applied when creating snapshot surface if the display rotation may be
// changed according to the activity orientation.
- assertTrue(mActivity.hasFixedRotationTransform());
- assertTrue(mActivity.mDisplayContent.isFixedRotationLaunchingApp(mActivity));
+ assertTrue(activity.hasFixedRotationTransform());
+ assertTrue(activity.mDisplayContent.isFixedRotationLaunchingApp(activity));
}
/**
@@ -1604,11 +1680,12 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testActivityReparentChangesProcessOverride() {
- final WindowProcessController wpc = mActivity.app;
- final Task initialTask = mActivity.getTask();
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
+ final Task initialTask = activity.getTask();
final Configuration initialConf =
- new Configuration(mActivity.getMergedOverrideConfiguration());
- assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ new Configuration(activity.getMergedOverrideConfiguration());
+ assertEquals(0, activity.getMergedOverrideConfiguration()
.diff(wpc.getRequestedOverrideConfiguration()));
assertTrue(wpc.registeredForActivityConfigChanges());
@@ -1621,22 +1698,23 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi);
// Reparent the activity and verify that config override changed.
- mActivity.reparent(newTask, 0 /* top */, "test");
- assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi);
- assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
+ activity.reparent(newTask, 0 /* top */, "test");
+ assertEquals(activity.getConfiguration().densityDpi, newConfig.densityDpi);
+ assertEquals(activity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
assertTrue(wpc.registeredForActivityConfigChanges());
assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
- assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ assertEquals(0, activity.getMergedOverrideConfiguration()
.diff(wpc.getRequestedOverrideConfiguration()));
}
@Test
public void testActivityReparentDoesntClearProcessOverride_TwoActivities() {
- final WindowProcessController wpc = mActivity.app;
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
final Configuration initialConf =
- new Configuration(mActivity.getMergedOverrideConfiguration());
- final Task initialTask = mActivity.getTask();
+ new Configuration(activity.getMergedOverrideConfiguration());
+ final Task initialTask = activity.getTask();
final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(initialTask)
.setUseProcess(wpc).build();
@@ -1660,7 +1738,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
// Reparent the first activity and verify that config override didn't change.
- mActivity.reparent(newTask, 1 /* top */, "test");
+ activity.reparent(newTask, 1 /* top */, "test");
assertTrue(wpc.registeredForActivityConfigChanges());
assertEquals(0, secondActivity.getMergedOverrideConfiguration()
.diff(wpc.getRequestedOverrideConfiguration()));
@@ -1703,67 +1781,90 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testFullscreenWindowCanTurnScreenOn() {
- mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- doReturn(true).when(mActivity).getTurnScreenOnFlag();
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(activity).getTurnScreenOnFlag();
- assertTrue(mActivity.canTurnScreenOn());
+ assertTrue(activity.canTurnScreenOn());
}
@Test
public void testFreeformWindowCanTurnScreenOn() {
- mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- doReturn(true).when(mActivity).getTurnScreenOnFlag();
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ doReturn(true).when(activity).getTurnScreenOnFlag();
- assertTrue(mActivity.canTurnScreenOn());
+ assertTrue(activity.canTurnScreenOn());
}
@Test
public void testGetLockTaskLaunchMode() {
+ final ActivityRecord activity = createActivityWithTask();
final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true);
- mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+ activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED,
- ActivityRecord.getLockTaskLaunchMode(mActivity.info, options));
+ ActivityRecord.getLockTaskLaunchMode(activity.info, options));
- mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+ activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
- ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+ ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
- mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+ activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT,
- ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+ ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
- mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
+ activity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+ activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS;
assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS,
- ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+ ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
- mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
+ activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER;
assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER,
- ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/));
+ ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/));
}
@Test
public void testProcessInfoUpdateWhenSetState() {
- spyOn(mActivity.app);
- verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */);
- verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */);
- verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */);
- verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */);
-
- mActivity.app.removeActivity(mActivity, true /* keepAssociation */);
- verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */);
- verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */);
- }
-
- private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate,
- boolean activityChange) {
- reset(mActivity.app);
- mActivity.setState(state, "test");
- verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(),
+ final ActivityRecord activity = createActivityWithTask();
+ activity.setState(INITIALIZING, "test");
+ spyOn(activity.app);
+ verifyProcessInfoUpdate(activity, RESUMED,
+ true /* shouldUpdate */, true /* activityChange */);
+ verifyProcessInfoUpdate(activity, PAUSED,
+ false /* shouldUpdate */, false /* activityChange */);
+ verifyProcessInfoUpdate(activity, STOPPED,
+ false /* shouldUpdate */, false /* activityChange */);
+ verifyProcessInfoUpdate(activity, STARTED,
+ true /* shouldUpdate */, true /* activityChange */);
+
+ activity.app.removeActivity(activity, true /* keepAssociation */);
+ verifyProcessInfoUpdate(activity, DESTROYING,
+ true /* shouldUpdate */, false /* activityChange */);
+ verifyProcessInfoUpdate(activity, DESTROYED,
+ true /* shouldUpdate */, false /* activityChange */);
+ }
+
+ private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+ boolean shouldUpdate, boolean activityChange) {
+ reset(activity.app);
+ activity.setState(state, "test");
+ verify(activity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(),
eq(activityChange), anyBoolean(), anyBoolean());
}
+ private ActivityRecord createActivityWithTask() {
+ return new ActivityBuilder(mAtm).setCreateTask(true).setOnTop(true).build();
+ }
+
+ private ActivityRecord createActivityWith2LevelTask() {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setCreateParentTask(true).setCreateActivity(true).build();
+ return task.getTopNonFinishingActivity();
+ }
+
/**
* Creates an activity on display. For non-default display request it will also create a new
* display with custom DisplayInfo.
@@ -1777,9 +1878,7 @@ public class ActivityRecordTests extends WindowTestsBase {
display = new TestDisplayContent.Builder(mAtm, 2000, 1000).setDensityDpi(300)
.setPosition(DisplayContent.POSITION_TOP).build();
}
- final Task stack = display.getDefaultTaskDisplayArea()
- .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index d872eb856a50..8ccbb8fe62ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -43,6 +43,7 @@ import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
@@ -91,59 +92,58 @@ import java.util.function.Consumer;
@RunWith(WindowTestRunner.class)
public class ActivityStackTests extends WindowTestsBase {
private TaskDisplayArea mDefaultTaskDisplayArea;
- private Task mStack;
- private Task mTask;
@Before
public void setUp() throws Exception {
mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
- mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED,
- ACTIVITY_TYPE_STANDARD, true /* onTop */);
- spyOn(mStack);
- mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build();
}
@Test
public void testResumedActivity() {
- final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
- assertNull(mStack.getResumedActivity());
+ final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = r.getTask();
+ assertNull(task.getResumedActivity());
r.setState(RESUMED, "testResumedActivity");
- assertEquals(r, mStack.getResumedActivity());
+ assertEquals(r, task.getResumedActivity());
r.setState(PAUSING, "testResumedActivity");
- assertNull(mStack.getResumedActivity());
+ assertNull(task.getResumedActivity());
}
@Test
public void testResumedActivityFromTaskReparenting() {
- final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ final ActivityRecord r = new ActivityBuilder(mAtm)
+ .setCreateTask(true).setParentTask(parentTask).build();
+ final Task task = r.getTask();
// Ensure moving task between two stacks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
- assertEquals(r, mStack.getResumedActivity());
+ assertEquals(r, parentTask.getResumedActivity());
- final Task destStack = mDefaultTaskDisplayArea.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
- mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT,
+ final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
- assertNull(mStack.getResumedActivity());
+ assertNull(parentTask.getResumedActivity());
assertEquals(r, destStack.getResumedActivity());
}
@Test
public void testResumedActivityFromActivityReparenting() {
- final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ final ActivityRecord r = new ActivityBuilder(mAtm)
+ .setCreateTask(true).setParentTask(parentTask).build();
+ final Task task = r.getTask();
// Ensure moving task between two stacks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
- assertEquals(r, mStack.getResumedActivity());
+ assertEquals(r, parentTask.getResumedActivity());
- final Task destStack = mDefaultTaskDisplayArea.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false, false,
+ final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build();
+ task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ false /* animate */, false /* deferResume*/,
"testResumedActivityFromActivityReparenting");
- assertNull(mStack.getResumedActivity());
+ assertNull(parentTask.getResumedActivity());
assertEquals(r, destStack.getResumedActivity());
}
@@ -292,7 +292,7 @@ public class ActivityStackTests extends WindowTestsBase {
public void testStopActivityWhenActivityDestroyed() {
final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
- mStack.moveToFront("testStopActivityWithDestroy");
+ r.getTask().moveToFront("testStopActivityWithDestroy");
r.stopIfPossible();
// Mostly testing to make sure there is a crash in the call part, so if we get here we are
// good-to-go!
@@ -327,7 +327,8 @@ public class ActivityStackTests extends WindowTestsBase {
targetActivity);
final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
aliasActivity);
- final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build();
+ final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
+ final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build();
task.origActivity = alias;
task.realActivity = target;
new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity(
@@ -337,14 +338,14 @@ public class ActivityStackTests extends WindowTestsBase {
final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
target).setTargetActivity(targetActivity).build();
RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
- result.process(r1, mStack);
+ result.process(r1, parentTask);
assertThat(result.mRecord).isNotNull();
// Using alias activity to find task.
final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
alias).setTargetActivity(targetActivity).build();
result = new RootWindowContainer.FindTaskResult();
- result.process(r2, mStack);
+ result.process(r2, parentTask);
assertThat(result.mRecord).isNotNull();
}
@@ -735,8 +736,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task fullscreenStack = createStackForShouldBeVisibleTest(
@@ -755,8 +754,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task fullscreenStack = createStackForShouldBeVisibleTest(
@@ -775,8 +772,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task fullscreenStack = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -795,8 +790,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
@@ -822,8 +815,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void
testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
@@ -846,8 +837,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindStack_BehindHomeStack() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -869,8 +858,6 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testMoveHomeStackBehindStack() {
- mDefaultTaskDisplayArea.removeStack(mStack);
-
final Task fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
@@ -1019,11 +1006,12 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testFinishDisabledPackageActivities_FinishAliveActivities() {
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
firstActivity.setState(STOPPED, "testFinishDisabledPackageActivities");
secondActivity.setState(RESUMED, "testFinishDisabledPackageActivities");
- mStack.mResumedActivity = secondActivity;
+ task.mResumedActivity = secondActivity;
// Note the activities have non-null ActivityRecord.app, so it won't remove directly.
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
@@ -1039,10 +1027,11 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testFinishDisabledPackageActivities_RemoveNonAliveActivities() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
// The overlay activity is not in the disabled package but it is in the same task.
- final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(mTask)
+ final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task)
.setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
// If the task only remains overlay activity, the task should also be removed.
// See {@link ActivityStack#removeFromHistory}.
@@ -1053,7 +1042,7 @@ public class ActivityStackTests extends WindowTestsBase {
activity.app = null;
overlayActivity.app = null;
- assertEquals(2, mTask.getChildCount());
+ assertEquals(2, task.getChildCount());
mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process(
activity.packageName, null /* filterByClasses */, true /* doit */,
@@ -1062,14 +1051,14 @@ public class ActivityStackTests extends WindowTestsBase {
// Although the overlay activity is in another package, the non-overlay activities are
// removed from the task. Since the overlay activity should be removed as well, the task
// should be empty.
- assertFalse(mTask.hasChild());
- assertFalse(mStack.hasChild());
+ assertFalse(task.hasChild());
}
@Test
public void testHandleAppDied() {
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
// Making the first activity a task overlay means it will be removed from the task's
// activities as well once second activity is removed as handleAppDied processes the
@@ -1080,17 +1069,17 @@ public class ActivityStackTests extends WindowTestsBase {
// second activity will be immediately removed as it has no state.
secondActivity.setSavedState(null /* savedState */);
- assertEquals(2, mTask.getChildCount());
+ assertEquals(2, task.getChildCount());
secondActivity.app.handleAppDied();
- assertFalse(mTask.hasChild());
- assertFalse(mStack.hasChild());
+ assertFalse(task.hasChild());
}
@Test
public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
activity.launchCount = 1;
@@ -1098,13 +1087,13 @@ public class ActivityStackTests extends WindowTestsBase {
activity.app.handleAppDied();
- assertEquals(1, mTask.getChildCount());
- assertEquals(1, mStack.getChildCount());
+ assertEquals(1, task.getChildCount());
}
@Test
public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
activity.launchCount = 3;
@@ -1112,13 +1101,13 @@ public class ActivityStackTests extends WindowTestsBase {
activity.app.handleAppDied();
- assertFalse(mTask.hasChild());
- assertFalse(mStack.hasChild());
+ assertFalse(task.hasChild());
}
@Test
public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
activity.launchCount = 1;
@@ -1126,13 +1115,13 @@ public class ActivityStackTests extends WindowTestsBase {
activity.app.handleAppDied();
- assertEquals(1, mTask.getChildCount());
- assertEquals(1, mStack.getChildCount());
+ assertEquals(1, task.getChildCount());
}
@Test
public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
activity.launchCount = 3;
@@ -1140,22 +1129,22 @@ public class ActivityStackTests extends WindowTestsBase {
activity.app.handleAppDied();
- assertFalse(mTask.hasChild());
- assertFalse(mStack.hasChild());
+ assertFalse(task.hasChild());
}
@Test
public void testCompletePauseOnResumeWhilePausingActivity() {
- final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
doReturn(true).when(bottomActivity).attachedToProcess();
- mStack.mPausingActivity = null;
- mStack.mResumedActivity = bottomActivity;
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ task.mPausingActivity = null;
+ task.mResumedActivity = bottomActivity;
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
- mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
+ task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
"test");
- verify(mStack).completePauseLocked(anyBoolean(), eq(topActivity));
+ verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
}
@Test
@@ -1234,10 +1223,11 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testStackOrderChangedOnRemoveStack() {
+ final Task task = new TaskBuilder(mSupervisor).build();
StackOrderChangedListener listener = new StackOrderChangedListener();
mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
try {
- mDefaultTaskDisplayArea.removeStack(mStack);
+ mDefaultTaskDisplayArea.removeStack(task);
} finally {
mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
}
@@ -1246,13 +1236,14 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testStackOrderChangedOnAddPositionStack() {
- mDefaultTaskDisplayArea.removeStack(mStack);
+ final Task task = new TaskBuilder(mSupervisor).build();
+ mDefaultTaskDisplayArea.removeStack(task);
StackOrderChangedListener listener = new StackOrderChangedListener();
mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
try {
- mStack.mReparenting = true;
- mDefaultTaskDisplayArea.addChild(mStack, 0);
+ task.mReparenting = true;
+ mDefaultTaskDisplayArea.addChild(task, 0);
} finally {
mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
}
@@ -1284,20 +1275,21 @@ public class ActivityStackTests extends WindowTestsBase {
spyOn(starter);
doReturn(ActivityManager.START_SUCCESS).when(starter).execute();
- final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask)
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task)
.setUid(firstActivity.getUid() + 1).build();
doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString());
final IApplicationThread thread = secondActivity.app.getThread();
secondActivity.app.setThread(null);
// This should do nothing from a non-attached caller.
- assertFalse(mStack.navigateUpTo(secondActivity /* source record */,
+ assertFalse(task.navigateUpTo(secondActivity /* source record */,
firstActivity.intent /* destIntent */, null /* destGrants */,
0 /* resultCode */, null /* resultData */, null /* resultGrants */));
secondActivity.app.setThread(thread);
- assertTrue(mStack.navigateUpTo(secondActivity /* source record */,
+ assertTrue(task.navigateUpTo(secondActivity /* source record */,
firstActivity.intent /* destIntent */, null /* destGrants */,
0 /* resultCode */, null /* resultData */, null /* resultGrants */));
// The firstActivity uses default launch mode, so the activities between it and itself will
@@ -1313,9 +1305,10 @@ public class ActivityStackTests extends WindowTestsBase {
final String affinity = "affinity";
final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity)
.setUid(Binder.getCallingUid()).setCreateTask(true).build();
- activity.getTask().affinity = activity.taskAffinity;
+ final Task task = activity.getTask();
+ task.affinity = activity.taskAffinity;
- assertFalse(mStack.shouldUpRecreateTaskLocked(activity, affinity));
+ assertFalse(task.shouldUpRecreateTaskLocked(activity, affinity));
}
@Test
@@ -1323,21 +1316,23 @@ public class ActivityStackTests extends WindowTestsBase {
final String affinity = "affinity";
final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity)
.setUid(Binder.getCallingUid()).setCreateTask(true).build();
- activity.getTask().affinity = activity.taskAffinity;
+ final Task task = activity.getTask();
+ task.affinity = activity.taskAffinity;
final String fakeAffinity = activity.getUid() + activity.taskAffinity;
- assertTrue(mStack.shouldUpRecreateTaskLocked(activity, fakeAffinity));
+ assertTrue(task.shouldUpRecreateTaskLocked(activity, fakeAffinity));
}
@Test
public void testResetTaskWithFinishingActivities() {
- final ActivityRecord taskTop = new ActivityBuilder(mAtm).setTask(mStack).build();
+ final ActivityRecord taskTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = taskTop.getTask();
// Make all activities in the task are finishing to simulate Task#getTopActivity
// returns null.
taskTop.finishing = true;
final ActivityRecord newR = new ActivityBuilder(mAtm).build();
- final ActivityRecord result = mStack.resetTaskIfNeeded(taskTop, newR);
+ final ActivityRecord result = task.resetTaskIfNeeded(taskTop, newR);
assertThat(result).isEqualTo(taskTop);
}
@@ -1345,14 +1340,15 @@ public class ActivityStackTests extends WindowTestsBase {
public void testIterateOccludedActivity() {
final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
- final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
// Top activity occludes bottom activity.
- doReturn(true).when(mStack).shouldBeVisible(any());
+ doReturn(true).when(task).shouldBeVisible(any());
assertTrue(topActivity.shouldBeVisible());
assertFalse(bottomActivity.shouldBeVisible());
- mStack.forAllOccludedActivities(handleOccludedActivity);
+ task.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).containsExactly(bottomActivity);
// Top activity doesn't occlude parent, so the bottom activity is not occluded.
@@ -1360,18 +1356,18 @@ public class ActivityStackTests extends WindowTestsBase {
assertTrue(bottomActivity.shouldBeVisible());
occludedActivities.clear();
- mStack.forAllOccludedActivities(handleOccludedActivity);
+ task.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).isEmpty();
// A finishing activity should not occlude other activities behind.
- final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
finishingActivity.finishing = true;
doCallRealMethod().when(finishingActivity).occludesParent();
assertTrue(topActivity.shouldBeVisible());
assertTrue(bottomActivity.shouldBeVisible());
occludedActivities.clear();
- mStack.forAllOccludedActivities(handleOccludedActivity);
+ task.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).isEmpty();
}
@@ -1385,8 +1381,9 @@ public class ActivityStackTests extends WindowTestsBase {
// Start 2 activities that their processes have not yet started.
final ActivityRecord[] activities = new ActivityRecord[2];
mSupervisor.beginDeferResume();
+ final Task task = new TaskBuilder(mSupervisor).build();
for (int i = 0; i < activities.length; i++) {
- final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build();
+ final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
activities[i] = r;
doReturn(null).when(mAtm).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
@@ -1405,7 +1402,7 @@ public class ActivityStackTests extends WindowTestsBase {
// Assume the top activity is going to resume and
// {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown
// visibility records that are occluded.
- mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+ task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
// Assume the top activity relayouted, just remove it directly.
unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
// All unresolved records should be removed.
@@ -1414,15 +1411,16 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testNonTopVisibleActivityNotResume() {
+ final Task task = new TaskBuilder(mSupervisor).build();
final ActivityRecord nonTopVisibleActivity =
- new ActivityBuilder(mAtm).setTask(mTask).build();
- new ActivityBuilder(mAtm).setTask(mTask).build();
+ new ActivityBuilder(mAtm).setTask(task).build();
+ new ActivityBuilder(mAtm).setTask(task).build();
doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
anyBoolean());
- mStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
anyBoolean());
@@ -1436,16 +1434,17 @@ public class ActivityStackTests extends WindowTestsBase {
private void verifyShouldSleepActivities(boolean focusedStack,
boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
boolean expected) {
+ final Task task = new TaskBuilder(mSupervisor).build();
final DisplayContent display = mock(DisplayContent.class);
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
display.isDefaultDisplay = isDefaultDisplay;
- mStack.mDisplayContent = display;
+ task.mDisplayContent = display;
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
- doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
+ doReturn(focusedStack).when(task).isFocusedStackOnDisplay();
- assertEquals(expected, mStack.shouldSleepActivities());
+ assertEquals(expected, task.shouldSleepActivities());
}
private static class StackOrderChangedListener
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f607448c09a0..f745ffbc680c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -126,6 +126,7 @@ public class ActivityStarterTests extends WindowTestsBase {
private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
private static final int UNIMPORTANT_UID = 12345;
private static final int UNIMPORTANT_UID2 = 12346;
+ private static final int CURRENT_IME_UID = 12347;
@Before
public void setUp() throws Exception {
@@ -315,6 +316,12 @@ public class ActivityStarterTests extends WindowTestsBase {
return prepareStarter(launchFlags, mockGetLaunchStack, LAUNCH_MULTIPLE);
}
+ private void setupImeWindow() {
+ final WindowState imeWindow = createWindow(null, W_INPUT_METHOD,
+ "mImeWindow", CURRENT_IME_UID);
+ mDisplayContent.mInputMethodWindow = imeWindow;
+ }
+
/**
* Creates a {@link ActivityStarter} with default parameters and necessary mocks.
*
@@ -654,6 +661,14 @@ public class ActivityStarterTests extends WindowTestsBase {
UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
false, false, false, false, true);
+
+ setupImeWindow();
+ runAndVerifyBackgroundActivityStartsSubtest(
+ "disallowed_callingPackageNameIsIme_notAborted", false,
+ CURRENT_IME_UID, false, PROCESS_STATE_TOP + 1,
+ UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+ false, false, false, false, false);
+
}
private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index f61253c77a8d..017ed883e2bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -19,8 +19,6 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -44,7 +42,6 @@ import android.view.Display;
import androidx.test.filters.MediumTest;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,13 +55,6 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityTaskSupervisorTests extends WindowTestsBase {
- private Task mFullscreenTask;
-
- @Before
- public void setUp() throws Exception {
- mFullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- }
/**
* Ensures that an activity is removed from the stopping activities list once it is resumed.
@@ -72,7 +62,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
@Test
public void testStoppingActivityRemovedWhenResumed() {
final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(mFullscreenTask).build();
+ .setCreateTask(true).build();
mSupervisor.mStoppingActivities.add(firstActivity);
firstActivity.completeResumeLocked();
@@ -86,7 +76,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
@Test
public void testReportWaitingActivityLaunchedIfNeeded() {
final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
- .setTask(mFullscreenTask).build();
+ .setCreateTask(true).build();
final WaitResult taskToFrontWait = new WaitResult();
mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
@@ -153,7 +143,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
@Test
public void testNotifyTaskFocusChanged() {
final ActivityRecord fullScreenActivityA = new ActivityBuilder(mAtm).setCreateTask(true)
- .setParentTask(mFullscreenTask).build();
+ .build();
final Task taskA = fullScreenActivityA.getTask();
final TaskChangeNotificationController taskChangeNotifier =
@@ -166,7 +156,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
reset(taskChangeNotifier);
final ActivityRecord fullScreenActivityB = new ActivityBuilder(mAtm).setCreateTask(true)
- .setParentTask(mFullscreenTask).build();
+ .build();
final Task taskB = fullScreenActivityB.getTask();
mAtm.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
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 87a5985d507d..91b9449eddb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -53,14 +53,12 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class AppChangeTransitionTests extends WindowTestsBase {
- private Task mStack;
private Task mTask;
private ActivityRecord mActivity;
public void setUpOnDisplay(DisplayContent dc) {
mActivity = createActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
mTask = mActivity.getTask();
- mStack = mTask.getRootTask();
// Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -143,7 +141,7 @@ public class AppChangeTransitionTests extends WindowTestsBase {
// Reparenting to a display with different windowing mode may trigger
// a change transition internally, but it should be cleaned-up once
// the display change is complete.
- mStack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true);
+ mTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true);
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index bc4f16eccb0a..8cad56a45518 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -20,10 +20,12 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -74,11 +76,14 @@ public class AppTransitionControllerTest extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
translucentOpening.setVisible(false);
+ mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
+
assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_OLD_TASK_OPEN));
+ AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null, null));
}
@Test
@@ -89,10 +94,12 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord translucentClosing = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentClosing.setOccludesParent(false);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
mDisplayContent.mClosingApps.add(translucentClosing);
assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_OLD_TASK_CLOSE));
+ AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null, null));
}
@Test
@@ -104,11 +111,13 @@ public class AppTransitionControllerTest extends WindowTestsBase {
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentOpening.setOccludesParent(false);
translucentOpening.setVisible(false);
+ mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE));
+ AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null, null));
}
@Test
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 850e72e8e94b..6ca69bf974d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -18,11 +18,12 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -71,40 +72,58 @@ public class AppTransitionTests extends WindowTestsBase {
@Test
public void testKeyguardOverride() {
- mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
+ final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ final ActivityRecord activity = createActivityRecord(dc);
+
+ mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+ mDc.mOpeningApps.add(activity);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null /* wallpaperTarget */, null /* oldWallpaper */));
}
@Test
public void testKeyguardKeep() {
- mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
- }
-
- @Test
- public void testForceOverride() {
- mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
- assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransitionOld());
+ final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ final ActivityRecord activity = createActivityRecord(dc);
+
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+ mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.mOpeningApps.add(activity);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null /* wallpaperTarget */, null /* oldWallpaper */));
}
@Test
public void testCrashing() {
- mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransitionOld());
+ final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ final ActivityRecord activity = createActivityRecord(dc);
+
+ mDc.prepareAppTransition(TRANSIT_OPEN);
+ mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+ mDc.mClosingApps.add(activity);
+ assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null /* wallpaperTarget */, null /* oldWallpaper */));
}
@Test
public void testKeepKeyguard_withCrashing() {
- mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
+ final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ final ActivityRecord activity = createActivityRecord(dc);
+
+ mDc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY);
+ mDc.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+ mDc.mClosingApps.add(activity);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+ AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ null /* wallpaperTarget */, null /* oldWallpaper */));
}
@Test
@@ -123,12 +142,8 @@ public class AppTransitionTests extends WindowTestsBase {
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
- dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
- dc2.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransitionOld());
+ dc1.prepareAppTransition(TRANSIT_OPEN);
+ dc2.prepareAppTransition(TRANSIT_CLOSE);
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
activity1.setVisibility(true, false);
@@ -156,9 +171,8 @@ public class AppTransitionTests extends WindowTestsBase {
dc1.mClosingApps.add(activity1);
assertTrue(dc1.mClosingApps.size() > 0);
- dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
+ dc1.prepareAppTransition(TRANSIT_OPEN);
+ assertTrue(dc1.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
assertTrue(dc1.mAppTransition.isTransitionSet());
dc1.mOpeningApps.add(activity1);
@@ -199,9 +213,8 @@ public class AppTransitionTests extends WindowTestsBase {
// Simulate activity finish flows to prepare app transition & set visibility,
// make sure transition is set as expected.
- dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransitionOld());
+ dc.prepareAppTransition(TRANSIT_CLOSE);
+ assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_CLOSE));
dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
exitingActivity.setVisibility(false, false);
assertTrue(dc.mClosingApps.size() > 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index f77454d440f9..d899ebe4bc1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -65,7 +65,6 @@ import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,94 +81,87 @@ import java.util.ArrayList;
@RunWith(WindowTestRunner.class)
public class AppWindowTokenTests extends WindowTestsBase {
- Task mStack;
- Task mTask;
- ActivityRecord mActivity;
-
private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
- @Before
- public void setUp() throws Exception {
- mStack = createTaskStackOnDisplay(mDisplayContent);
- mTask = createTaskInStack(mStack, 0 /* userId */);
- mActivity = createNonAttachedActivityRecord(mDisplayContent);
-
- mTask.addChild(mActivity, 0);
- }
-
@Test
@Presubmit
public void testAddWindow_Order() {
- assertEquals(0, mActivity.getChildCount());
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ assertEquals(0, activity.getChildCount());
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, mActivity, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, mActivity, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4");
// Should not contain the windows that were added above.
- assertEquals(4, mActivity.getChildCount());
- assertTrue(mActivity.mChildren.contains(win1));
- assertTrue(mActivity.mChildren.contains(startingWin));
- assertTrue(mActivity.mChildren.contains(baseWin));
- assertTrue(mActivity.mChildren.contains(win4));
+ assertEquals(4, activity.getChildCount());
+ assertTrue(activity.mChildren.contains(win1));
+ assertTrue(activity.mChildren.contains(startingWin));
+ assertTrue(activity.mChildren.contains(baseWin));
+ assertTrue(activity.mChildren.contains(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, mActivity.mChildren.peekLast());
+ assertEquals(startingWin, activity.mChildren.peekLast());
// The base application window should be below all other windows.
- assertEquals(baseWin, mActivity.mChildren.peekFirst());
- mActivity.removeImmediately();
+ assertEquals(baseWin, activity.mChildren.peekFirst());
+ activity.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() {
- assertNull(mActivity.findMainWindow());
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ assertNull(activity.findMainWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window12");
- assertEquals(window1, mActivity.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12");
+ assertEquals(window1, activity.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, mActivity.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
+ assertEquals(window1, activity.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity,
"window2");
- assertEquals(window2, mActivity.findMainWindow());
- mActivity.removeImmediately();
+ assertEquals(window2, activity.findMainWindow());
+ activity.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenOpaqueWindow() {
- assertNull(mActivity.getTopFullscreenOpaqueWindow());
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ assertNull(activity.getTopFullscreenOpaqueWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, mActivity, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, mActivity, "window12");
- assertEquals(window12, mActivity.getTopFullscreenOpaqueWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12");
+ assertEquals(window12, activity.getTopFullscreenOpaqueWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, mActivity.getTopFullscreenOpaqueWindow());
+ assertEquals(window11, activity.getTopFullscreenOpaqueWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, mActivity.getTopFullscreenOpaqueWindow());
+ assertEquals(window1, activity.getTopFullscreenOpaqueWindow());
window1.mAttrs.alpha = 0f;
- assertNull(mActivity.getTopFullscreenOpaqueWindow());
- mActivity.removeImmediately();
+ assertNull(activity.getTopFullscreenOpaqueWindow());
+ activity.removeImmediately();
}
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
@FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(mDisplayContent).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final TestWindowState appWindow = createWindowState(attrs, mActivity);
- mActivity.addWindow(appWindow);
+ final TestWindowState appWindow = createWindowState(attrs, activity);
+ activity.addWindow(appWindow);
// Set initial orientation and update.
- mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
mDisplayContent.updateOrientation(
mDisplayContent.getRequestedOverrideConfiguration(),
null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -177,7 +169,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- mActivity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ activity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
mDisplayContent.updateOrientation(
mDisplayContent.getRequestedOverrideConfiguration(),
null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -192,14 +184,17 @@ public class AppWindowTokenTests extends WindowTestsBase {
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testLandscapeSeascapeRotationByPolicy() {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(mDisplayContent).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
// This instance has been spied in {@link TestDisplayContent}.
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("RotationByPolicy");
- final TestWindowState appWindow = createWindowState(attrs, mActivity);
- mActivity.addWindow(appWindow);
+ final TestWindowState appWindow = createWindowState(attrs, activity);
+ activity.addWindow(appWindow);
// Set initial orientation and update.
performRotation(displayRotation, Surface.ROTATION_90);
@@ -220,48 +215,53 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
@Presubmit
public void testGetOrientation() {
- mActivity.setVisible(true);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity.setVisible(true);
- mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mActivity.setOccludesParent(false);
+ activity.setOccludesParent(false);
// Can specify orientation if app doesn't occludes parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation());
- mActivity.setOccludesParent(true);
- mActivity.setVisible(false);
+ activity.setOccludesParent(true);
+ activity.setVisible(false);
// Can not specify orientation if app isn't visible even though it occludes parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
- mActivity.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ activity.getOrientation(SCREEN_ORIENTATION_BEHIND));
}
@Test
@Presubmit
public void testKeyguardFlagsDuringRelaunch() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final TestWindowState appWindow = createWindowState(attrs, mActivity);
+ final TestWindowState appWindow = createWindowState(attrs, activity);
// Add window with show when locked flag
- mActivity.addWindow(appWindow);
- assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+ activity.addWindow(appWindow);
+ assertTrue(activity.containsShowWhenLockedWindow()
+ && activity.containsDismissKeyguardWindow());
// Start relaunching
- mActivity.startRelaunching();
- assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+ activity.startRelaunching();
+ assertTrue(activity.containsShowWhenLockedWindow()
+ && activity.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- mActivity.removeChild(appWindow);
- assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
+ activity.removeChild(appWindow);
+ assertTrue(activity.containsShowWhenLockedWindow()
+ && activity.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- mActivity.finishRelaunching();
- assertFalse(
- mActivity.containsShowWhenLockedWindow() || mActivity.containsDismissKeyguardWindow());
+ activity.finishRelaunching();
+ assertFalse(activity.containsShowWhenLockedWindow()
+ || activity.containsDismissKeyguardWindow());
}
@Test
@@ -281,17 +281,18 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testSetOrientation() {
- mActivity.setVisible(true);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity.setVisible(true);
// Assert orientation is unspecified to start.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.getOrientation());
- mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
+ activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation());
- mDisplayContent.removeAppToken(mActivity.token);
+ mDisplayContent.removeAppToken(activity.token);
// Assert orientation is unset to after container is removed.
- assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
// Reset display frozen state
mWm.mDisplayFrozen = false;
@@ -300,14 +301,15 @@ public class AppWindowTokenTests extends WindowTestsBase {
@UseTestDisplay
@Test
public void testRespectTopFullscreenOrientation() {
- final Configuration displayConfig = mActivity.mDisplayContent.getConfiguration();
- final Configuration activityConfig = mActivity.getConfiguration();
- mActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Configuration displayConfig = activity.mDisplayContent.getConfiguration();
+ final Configuration activityConfig = activity.getConfiguration();
+ activity.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation);
- final ActivityRecord topActivity = createActivityRecord(mTask);
+ final ActivityRecord topActivity = createActivityRecord(activity.getTask());
topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertEquals(Configuration.ORIENTATION_LANDSCAPE, displayConfig.orientation);
@@ -322,32 +324,36 @@ public class AppWindowTokenTests extends WindowTestsBase {
@UseTestDisplay
@Test
public void testReportOrientationChange() {
- mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ final Task task = new TaskBuilder(mSupervisor)
+ .setDisplay(mDisplayContent).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
+ activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
mDisplayContent.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
- reset(mTask);
- mActivity.reportDescendantOrientationChangeIfNeeded();
- verify(mTask).onConfigurationChanged(any(Configuration.class));
+ reset(task);
+ activity.reportDescendantOrientationChangeIfNeeded();
+ verify(task).onConfigurationChanged(any(Configuration.class));
}
@Test
@FlakyTest(bugId = 131176283)
public void testCreateRemoveStartingWindow() {
- mActivity.addStartingWindow(mPackageName,
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ activity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false);
waitUntilHandlersIdle();
- assertHasStartingWindow(mActivity);
- mActivity.removeStartingWindow();
+ assertHasStartingWindow(activity);
+ activity.removeStartingWindow();
waitUntilHandlersIdle();
- assertNoStartingWindow(mActivity);
+ assertNoStartingWindow(activity);
}
@Test
public void testAddRemoveRace() {
// There was once a race condition between adding and removing starting windows
- final ActivityRecord appToken = createIsolatedTestActivityRecord();
+ final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
for (int i = 0; i < 1000; i++) {
appToken.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -360,8 +366,8 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindow() {
- final ActivityRecord activity1 = createIsolatedTestActivityRecord();
- final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false);
@@ -376,8 +382,8 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowWhileCreating() {
- final ActivityRecord activity1 = createIsolatedTestActivityRecord();
- final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
() -> {
// Surprise, ...! Transfer window in the middle of the creation flow.
@@ -396,8 +402,8 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowCanAnimate() {
- final ActivityRecord activity1 = createIsolatedTestActivityRecord();
- final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false);
@@ -419,34 +425,34 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowFromFinishingActivity() {
- mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity.getTask();
+ activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
"Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */,
null /* transferFrom */, true /* newTask */, true /* taskSwitch */,
false /* processRunning */, false /* allowTaskSnapshot */,
false /* activityCreate */);
waitUntilHandlersIdle();
- assertHasStartingWindow(mActivity);
- mActivity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
+ assertHasStartingWindow(activity);
+ activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
- doCallRealMethod().when(mStack).startActivityLocked(
+ doCallRealMethod().when(task).startActivityLocked(
any(), any(), anyBoolean(), anyBoolean(), any());
// Make mVisibleSetFromTransferredStartingWindow true.
- final ActivityRecord middle = new ActivityBuilder(mWm.mAtmService)
- .setTask(mTask).build();
- mStack.startActivityLocked(middle, null /* focusedTopActivity */,
+ final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
+ task.startActivityLocked(middle, null /* focusedTopActivity */,
false /* newTask */, false /* keepCurTransition */, null /* options */);
middle.makeFinishingLocked();
- assertNull(mActivity.mStartingWindow);
+ assertNull(activity.mStartingWindow);
assertHasStartingWindow(middle);
- final ActivityRecord top = new ActivityBuilder(mWm.mAtmService)
- .setTask(mTask).build();
+ final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build();
// Expect the visibility should be updated to true when transferring starting window from
// a visible activity.
top.setVisible(false);
// The finishing middle should be able to transfer starting window to top.
- mStack.startActivityLocked(top, null /* focusedTopActivity */,
+ task.startActivityLocked(top, null /* focusedTopActivity */,
false /* newTask */, false /* keepCurTransition */, null /* options */);
assertNull(middle.mStartingWindow);
@@ -459,10 +465,12 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindowSetFixedRotation() {
- final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task task = activity.getTask();
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.setVisible(false);
- mTask.positionChildAt(topActivity, POSITION_TOP);
- mActivity.addStartingWindow(mPackageName,
+ task.positionChildAt(topActivity, POSITION_TOP);
+ activity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false);
waitUntilHandlersIdle();
@@ -470,38 +478,25 @@ public class AppWindowTokenTests extends WindowTestsBase {
// Make activities to have different rotation from it display and set fixed rotation
// transform to activity1.
int rotation = (mDisplayContent.getRotation() + 1) % 4;
- mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation);
+ mDisplayContent.setFixedRotationLaunchingApp(activity, rotation);
doReturn(rotation).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(topActivity);
// Make sure the fixed rotation transform linked to activity2 when adding starting window
// on activity2.
topActivity.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(),
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
false, false, false, true, false);
waitUntilHandlersIdle();
assertTrue(topActivity.hasFixedRotationTransform());
}
- private ActivityRecord createIsolatedTestActivityRecord() {
- final Task taskStack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(taskStack, 0 /* userId */);
- return createTestActivityRecordForGivenTask(task);
- }
-
- private ActivityRecord createTestActivityRecordForGivenTask(Task task) {
- final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
- task.addChild(activity, 0);
- waitUntilHandlersIdle();
- return activity;
- }
-
@Test
public void testTryTransferStartingWindowFromHiddenAboveToken() {
// Add two tasks on top of each other.
- final ActivityRecord activityTop = createIsolatedTestActivityRecord();
- final ActivityRecord activityBottom =
- createTestActivityRecordForGivenTask(activityTop.getTask());
+ final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build();
+ activityTop.getTask().addChild(activityBottom, 0);
// Add a starting window.
activityTop.addStartingWindow(mPackageName,
@@ -523,53 +518,58 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransitionAnimationBounds() {
removeGlobalMinSizeRestriction();
+ final Task task = new TaskBuilder(mSupervisor)
+ .setCreateParentTask(true).setCreateActivity(true).build();
+ final Task rootTask = task.getRootTask();
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
final Rect stackBounds = new Rect(0, 0, 1000, 600);
final Rect taskBounds = new Rect(100, 400, 600, 800);
// Set the bounds and windowing mode to window configuration directly, otherwise the
// testing setups may be discarded by configuration resolving.
- mStack.getWindowConfiguration().setBounds(stackBounds);
- mTask.getWindowConfiguration().setBounds(taskBounds);
- mActivity.getWindowConfiguration().setBounds(taskBounds);
+ rootTask.getWindowConfiguration().setBounds(stackBounds);
+ task.getWindowConfiguration().setBounds(taskBounds);
+ activity.getWindowConfiguration().setBounds(taskBounds);
// Check that anim bounds for freeform window match task bounds
- mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE));
+ task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_NONE));
// STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
// bounds animation layer.
- mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+ task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
// Even the activity is smaller than task and it is not aligned to the top-left corner of
// task, the animation bounds the same as task and position should be zero because in real
// case the letterbox will fill the remaining area in task.
final Rect halfBounds = new Rect(taskBounds);
halfBounds.scale(0.5f);
- mActivity.getWindowConfiguration().setBounds(halfBounds);
+ activity.getWindowConfiguration().setBounds(halfBounds);
final Point animationPosition = new Point();
- mActivity.getAnimationPosition(animationPosition);
+ activity.getAnimationPosition(animationPosition);
- assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+ assertEquals(taskBounds, activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
assertEquals(new Point(0, 0), animationPosition);
// STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
- mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
+ task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertEquals(rootTask.getBounds(), activity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
}
@Test
public void testHasStartingWindow() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
final WindowManager.LayoutParams attrs =
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
- final TestWindowState startingWindow = createWindowState(attrs, mActivity);
- mActivity.startingDisplayed = true;
- mActivity.addWindow(startingWindow);
- assertTrue("Starting window should be present", mActivity.hasStartingWindow());
- mActivity.startingDisplayed = false;
- assertTrue("Starting window should be present", mActivity.hasStartingWindow());
-
- mActivity.removeChild(startingWindow);
- assertFalse("Starting window should not be present", mActivity.hasStartingWindow());
+ final TestWindowState startingWindow = createWindowState(attrs, activity);
+ activity.startingDisplayed = true;
+ activity.addWindow(startingWindow);
+ assertTrue("Starting window should be present", activity.hasStartingWindow());
+ activity.startingDisplayed = false;
+ assertTrue("Starting window should be present", activity.hasStartingWindow());
+
+ activity.removeChild(startingWindow);
+ assertFalse("Starting window should not be present", activity.hasStartingWindow());
}
private void assertHasStartingWindow(ActivityRecord atoken) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index cfe956f77bd3..06a6882dc698 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -52,8 +50,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase {
private DisplayAreaGroup mDisplayAreaGroup;
private TaskDisplayArea mTaskDisplayArea;
- private Task mStack;
- private ActivityRecord mActivity;
@Before
public void setUp() {
@@ -65,9 +61,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase {
mTaskDisplayArea = new TaskDisplayArea(
mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1);
mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP);
- mStack = mTaskDisplayArea.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mActivity = new ActivityBuilder(mAtm).setTask(mStack).build();
mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea);
}
@@ -91,28 +84,31 @@ public class DisplayAreaGroupTest extends WindowTestsBase {
@Test
public void testGetRequestedOrientationForDisplay() {
+ final Task task = new TaskBuilder(mSupervisor)
+ .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any());
- mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
// Display is portrait, DisplayAreaGroup inherits that
mDisplayContent.setBounds(0, 0, 600, 900);
assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
- assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+ assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
.isEqualTo(ORIENTATION_PORTRAIT);
// DisplayAreaGroup is landscape, different from Display
mDisplayAreaGroup.setBounds(0, 0, 600, 450);
assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
- assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+ assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
.isEqualTo(ORIENTATION_LANDSCAPE);
// DisplayAreaGroup is portrait, same as Display
mDisplayAreaGroup.setBounds(0, 0, 300, 900);
assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
- assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */))
+ assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */))
.isEqualTo(ORIENTATION_PORTRAIT);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0f7a254ececf..d9217188582f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1186,8 +1186,7 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityRecord app = mAppWindow.mActivityRecord;
app.setVisible(false);
- mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
app.setRequestedOrientation(newOrientation);
@@ -1370,8 +1369,7 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
app.setState(Task.ActivityState.RESUMED, "test");
- mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
app.setRequestedOrientation(newOrientation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5641fe28a52f..70d47a580801 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -46,7 +46,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.view.DragEvent;
import android.view.IWindowSessionCallback;
@@ -59,6 +58,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import org.junit.After;
import org.junit.AfterClass;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index cc92dddb6de1..40f73b12f805 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -252,7 +252,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertNull(mController.mRecentScreenshotAnimator);
// Simulate the app transition finishing
- mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
+ mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0);
verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
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 ddc29f5dd516..a4bf5948c6a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -72,13 +72,11 @@ import java.util.ArrayList;
@Presubmit
@RunWith(WindowTestRunner.class)
public class SizeCompatTests extends WindowTestsBase {
- private Task mStack;
private Task mTask;
private ActivityRecord mActivity;
private void setUpApp(DisplayContent display) {
- mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
- mTask = mStack.getBottomMostTask();
+ mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
mActivity = mTask.getTopNonFinishingActivity();
}
@@ -97,7 +95,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
- resizeDisplay(mStack.mDisplayContent, 600, 1200);
+ resizeDisplay(mTask.mDisplayContent, 600, 1200);
// The visible activity should recompute configuration according to the last parent bounds.
mAtm.restartActivityProcessIfVisible(mActivity.appToken);
@@ -196,7 +194,7 @@ public class SizeCompatTests extends WindowTestsBase {
final int originalDpi = mActivity.getConfiguration().densityDpi;
// Move the non-resizable activity to the new display.
- mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+ mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
@@ -321,7 +319,7 @@ public class SizeCompatTests extends WindowTestsBase {
.setCanRotate(false).setNotch(notchHeight).build();
// Move the non-resizable activity to the new display.
- mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+ mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
assertEquals(origWidth, configBounds.width());
assertEquals(origHeight, configBounds.height());
@@ -363,7 +361,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
- assertEquals(ROTATION_270, mStack.getWindowConfiguration().getRotation());
+ assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
assertEquals(origBounds.width(), currentBounds.width());
// The notch is on horizontal side, so current height changes from 1460 to 1400.
@@ -436,7 +434,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testResetNonVisibleActivity() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
- final DisplayContent display = mStack.mDisplayContent;
+ final DisplayContent display = mTask.mDisplayContent;
// Resize the display so the activity is in size compatibility mode.
resizeDisplay(display, 900, 1800);
@@ -488,7 +486,7 @@ public class SizeCompatTests extends WindowTestsBase {
});
// Resize the display so that the activity exercises size-compat mode.
- resizeDisplay(mStack.mDisplayContent, 1000, 2500);
+ resizeDisplay(mTask.mDisplayContent, 1000, 2500);
// Expect the exact token when the activity is in size compatibility mode.
assertEquals(1, compatTokens.size());
@@ -501,7 +499,7 @@ public class SizeCompatTests extends WindowTestsBase {
activity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
+ mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
// Expect null token when switching to non-size-compat mode activity.
assertEquals(1, compatTokens.size());
@@ -525,13 +523,13 @@ public class SizeCompatTests extends WindowTestsBase {
// The non-resizable activity should not be size compat because it is on a resizable task
// in multi-window mode.
- mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
- mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
- mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
}
@@ -544,8 +542,7 @@ public class SizeCompatTests extends WindowTestsBase {
addStatusBar(mActivity.mDisplayContent);
mActivity.setVisible(false);
- mActivity.mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
- false /* alwaysKeepCurrent */);
+ mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mActivity.mDisplayContent.mOpeningApps.add(mActivity);
final float maxAspect = 1.8f;
prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
@@ -784,7 +781,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
final Rect activityBounds = new Rect(mActivity.getBounds());
- mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+ mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
// App still in size compat, and the bounds don't change.
verify(mActivity, never()).clearSizeCompatMode();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 4bd8edd44f5c..28ba710797c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -70,23 +70,22 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class TaskDisplayAreaTests extends WindowTestsBase {
- private Task mPinnedStack;
+ private Task mPinnedTask;
@Before
public void setUp() throws Exception {
- mPinnedStack = createTaskStackOnDisplay(
+ mPinnedTask = createTaskStackOnDisplay(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
// Stack should contain visible app window to be considered visible.
- final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
- assertFalse(mPinnedStack.isVisible());
+ assertFalse(mPinnedTask.isVisible());
final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent);
- pinnedTask.addChild(pinnedApp, 0 /* addPos */);
- assertTrue(mPinnedStack.isVisible());
+ mPinnedTask.addChild(pinnedApp, 0 /* addPos */);
+ assertTrue(mPinnedTask.isVisible());
}
@After
public void tearDown() throws Exception {
- mPinnedStack.removeImmediately();
+ mPinnedTask.removeImmediately();
}
@Test
@@ -118,19 +117,19 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
- final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
+ final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
assertThat(pinnedStackPos).isGreaterThan(stack2Pos);
assertThat(stack2Pos).isGreaterThan(stack1Pos);
- taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false);
+ taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false);
assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
- taskStackContainer.positionChildAt(1, mPinnedStack, false);
+ taskStackContainer.positionChildAt(1, mPinnedTask, false);
assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
}
@Test
@@ -141,16 +140,16 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final WindowContainer taskStackContainer = stack1.getParent();
final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
- final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
+ final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask);
assertThat(pinnedStackPos).isGreaterThan(stackPos);
taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
- assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 0b0341a1c8b7..fcd46a3b4024 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -41,10 +41,10 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.view.Surface;
import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
import org.junit.After;
import org.junit.AfterClass;
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 e0c72fb00060..3d8adbd215bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -919,7 +920,7 @@ public class WindowContainerTests extends WindowTestsBase {
}
}, 0, 0, false);
adapter.setCallingPidUid(123, 456);
- wc.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_TASK_OPEN, false);
+ wc.getDisplayContent().prepareAppTransition(TRANSIT_OPEN);
wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
spyOn(wc);
doReturn(true).when(wc).okToAnimate();
@@ -943,8 +944,7 @@ public class WindowContainerTests extends WindowTestsBase {
// Make sure animation finish callback will be received and reset animating state after
// animation finish.
- wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act,
- mDisplayContent.mOpeningApps);
+ wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act);
verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any());
assertFalse(wc.isAnimating());
assertFalse(act.isAnimating(PARENTS));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ee1034c561ab..ebbbc29b035c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -840,10 +840,13 @@ class WindowTestsBase extends SystemServiceTestsBase {
// to set it somewhere else since we can't mock resources.
doReturn(true).when(activity).occludesParent();
doReturn(true).when(activity).fillsParent();
+ mTask.addChild(activity);
if (mOnTop) {
+ // Move the task to front after activity added.
+ // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks
+ // (e.g. home stack).
mTask.moveToFront("createActivity");
}
- mTask.addChild(activity);
// Make visible by default...
activity.setVisible(true);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 632ad4c33e36..4bf93a26ec03 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -60,7 +60,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
-import java.util.function.BiFunction;
/**
* Helper for {@link SoundTrigger} APIs. Supports two types of models:
@@ -118,8 +117,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private PowerSaveModeListener mPowerSaveModeListener;
- private final BiFunction<Integer, SoundTrigger.StatusListener, SoundTriggerModule>
- mModuleProvider;
+ private final SoundTriggerModuleProvider mModuleProvider;
// Handler to process call state changes will delay to allow time for the audio
// and sound trigger HALs to process the end of call notifications
@@ -128,12 +126,32 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private static final int MSG_CALL_STATE_CHANGED = 0;
private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
- SoundTriggerHelper(Context context,
- @NonNull BiFunction<Integer, SoundTrigger.StatusListener,
- SoundTriggerModule> moduleProvider) {
+ /**
+ * Provider interface for retrieving SoundTriggerModule instances
+ */
+ public interface SoundTriggerModuleProvider {
+ /**
+ * Populate module properties for all available modules
+ *
+ * @param modules List of ModuleProperties to be populated
+ * @return Status int 0 on success.
+ */
+ int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules);
+
+ /**
+ * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()}
+ *
+ * @param moduleId Module ID
+ * @param statusListener Client listener to be associated with the returned module
+ * @return Module associated with moduleId
+ */
+ SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener);
+ }
+
+ SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) {
ArrayList <ModuleProperties> modules = new ArrayList<>();
mModuleProvider = moduleProvider;
- int status = SoundTrigger.listModules(modules);
+ int status = mModuleProvider.listModuleProperties(modules);
mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -272,7 +290,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private int prepareForRecognition(ModelData modelData) {
if (mModule == null) {
- mModule = mModuleProvider.apply(mModuleProperties.getId(), this);
+ mModule = mModuleProvider.getModule(mModuleProperties.getId(), this);
if (mModule == null) {
Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
return STATUS_ERROR;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 2a5bfce9bb33..bd678fd54063 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -51,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.hardware.soundtrigger.SoundTriggerModule;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
@@ -219,9 +220,20 @@ public class SoundTriggerService extends SystemService {
Identity originatorIdentity = IdentityContext.getNonNull();
return new SoundTriggerHelper(mContext,
- (moduleId, listener) -> SoundTrigger.attachModuleAsMiddleman(moduleId, listener,
- null,
- middlemanIdentity, originatorIdentity));
+ new SoundTriggerHelper.SoundTriggerModuleProvider() {
+ @Override
+ public int listModuleProperties(ArrayList<ModuleProperties> modules) {
+ return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity,
+ originatorIdentity);
+ }
+
+ @Override
+ public SoundTriggerModule getModule(int moduleId,
+ SoundTrigger.StatusListener statusListener) {
+ return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null,
+ middlemanIdentity, originatorIdentity);
+ }
+ });
}
class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 657a7dd84bdf..06c23de84ac3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -49,7 +48,6 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.permission.PermissionUtil;
@@ -66,7 +64,6 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManagerInternal;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
@@ -94,6 +91,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 964cf76d17f6..572aed3e6a11 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -447,6 +447,9 @@ public final class SmsManager {
* <code>RESULT_RIL_NO_RESOURCES</code><br>
* <code>RESULT_RIL_CANCELLED</code><br>
* <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
* the sentIntent may include the extra "errorCode" containing a radio technology specific
* value, generally only useful for troubleshooting.<br>
@@ -561,6 +564,9 @@ public final class SmsManager {
* <code>RESULT_RIL_NO_RESOURCES</code><br>
* <code>RESULT_RIL_CANCELLED</code><br>
* <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
* the sentIntent may include the extra "errorCode" containing a radio technology specific
* value, generally only useful for troubleshooting.<br>
@@ -962,6 +968,9 @@ public final class SmsManager {
* <code>RESULT_RIL_NO_RESOURCES</code><br>
* <code>RESULT_RIL_CANCELLED</code><br>
* <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
* the sentIntent may include the extra "errorCode" containing a radio technology specific
* value, generally only useful for troubleshooting.<br>
@@ -1220,6 +1229,9 @@ public final class SmsManager {
* <code>RESULT_RIL_NO_RESOURCES</code><br>
* <code>RESULT_RIL_CANCELLED</code><br>
* <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
* the sentIntent may include the extra "errorCode" containing a radio technology specific
* value, generally only useful for troubleshooting.<br>
@@ -1419,6 +1431,9 @@ public final class SmsManager {
* <code>RESULT_RIL_NO_RESOURCES</code><br>
* <code>RESULT_RIL_CANCELLED</code><br>
* <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
* the sentIntent may include the extra "errorCode" containing a radio technology specific
* value, generally only useful for troubleshooting.<br>
@@ -2298,7 +2313,10 @@ public final class SmsManager {
RESULT_RIL_OPERATION_NOT_ALLOWED,
RESULT_RIL_NO_RESOURCES,
RESULT_RIL_CANCELLED,
- RESULT_RIL_SIM_ABSENT
+ RESULT_RIL_SIM_ABSENT,
+ RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
+ RESULT_RIL_ACCESS_BARRED,
+ RESULT_RIL_BLOCKED_DUE_TO_CALL
})
@Retention(RetentionPolicy.SOURCE)
public @interface Result {}
@@ -2563,6 +2581,21 @@ public final class SmsManager {
*/
public static final int RESULT_RIL_SIM_ABSENT = 120;
+ /**
+ * 1X voice and SMS are not allowed simulteneously.
+ */
+ public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121;
+
+ /**
+ * Access is barred.
+ */
+ public static final int RESULT_RIL_ACCESS_BARRED = 122;
+
+ /**
+ * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity.
+ */
+ public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123;
+
// SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 83e63ef29e2c..904232b54b8f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -791,6 +791,13 @@ public class SubscriptionManager {
public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED;
/**
+ * Determines if the user has enabled cross SIM calling for this subscription.
+ *
+ * @hide
+ */
+ public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED;
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
new file mode 100644
index 000000000000..46bc4abde159
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable ApnThrottleStatus;
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java
new file mode 100644
index 000000000000..51461d17690a
--- /dev/null
+++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation;
+
+import java.util.Objects;
+
+/**
+ * Status information regarding the throttle status of an APN type.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ApnThrottleStatus implements Parcelable {
+ /**
+ * The APN type is not throttled.
+ */
+ public static final int THROTTLE_TYPE_NONE = 1;
+
+ /**
+ * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()}
+ * has reached {@link ApnThrottleStatus#getThrottleExpiryTimeMillis}
+ */
+ public static final int THROTTLE_TYPE_ELAPSED_TIME = 2;
+
+ /** {@hide} */
+ @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = {
+ ApnThrottleStatus.THROTTLE_TYPE_NONE,
+ ApnThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
+ })
+ public @interface ThrottleType {
+ }
+
+ /**
+ * The framework will not retry the APN type.
+ */
+ public static final int RETRY_TYPE_NONE = 1;
+
+ /**
+ * The next time the framework retries, it will attempt to establish a new connection.
+ */
+ public static final int RETRY_TYPE_NEW_CONNECTION = 2;
+
+ /**
+ * The next time the framework retires, it will retry to handover.
+ */
+ public static final int RETRY_TYPE_HANDOVER = 3;
+
+ /** {@hide} */
+ @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = {
+ ApnThrottleStatus.RETRY_TYPE_NONE,
+ ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
+ ApnThrottleStatus.RETRY_TYPE_HANDOVER,
+ })
+ public @interface RetryType {
+ }
+
+ private final int mSlotIndex;
+ private final @AccessNetworkConstants.TransportType int mTransportType;
+ private final @Annotation.ApnType int mApnType;
+ private final long mThrottleExpiryTimeMillis;
+ private final @RetryType int mRetryType;
+ private final @ThrottleType int mThrottleType;
+
+ /**
+ * The slot index that the status applies to.
+ *
+ * @return the slot index
+ */
+ public int getSlotIndex() {
+ return mSlotIndex;
+ }
+
+ /**
+ * The type of transport that the status applies to.
+ *
+ * @return the transport type
+ */
+ @AccessNetworkConstants.TransportType
+ public int getTransportType() {
+ return mTransportType;
+ }
+
+ /**
+ * The APN type that the status applies to.
+ *
+ * @return the apn type
+ */
+ @Annotation.ApnType
+ public int getApnType() {
+ return mApnType;
+ }
+
+ /**
+ * The type of throttle applied to the APN type.
+ *
+ * @return the throttle type
+ */
+ @ThrottleType
+ public int getThrottleType() {
+ return mThrottleType;
+ }
+
+ /**
+ * Indicates the type of request that the framework will make the next time it retries
+ * to call {@link IDataService#setupDataCall}.
+ *
+ * @return the retry type
+ */
+ @RetryType
+ public int getRetryType() {
+ return mRetryType;
+ }
+
+ /**
+ * Gets the time at which the throttle expires. The value is based off of
+ * {@link SystemClock#elapsedRealtime}.
+ *
+ * This value only applies when the throttle type is set to
+ * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+ *
+ * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+ *
+ * @return the time at which the throttle expires
+ */
+ @ElapsedRealtimeLong
+ public long getThrottleExpiryTimeMillis() {
+ return mThrottleExpiryTimeMillis;
+ }
+
+ private ApnThrottleStatus(int slotIndex,
+ @AccessNetworkConstants.TransportType int transportType,
+ @Annotation.ApnType int apnTypes,
+ @ThrottleType int throttleType,
+ long throttleExpiryTimeMillis,
+ @RetryType int retryType) {
+ mSlotIndex = slotIndex;
+ mTransportType = transportType;
+ mApnType = apnTypes;
+ mThrottleType = throttleType;
+ mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+ mRetryType = retryType;
+ }
+
+ private ApnThrottleStatus(@NonNull Parcel source) {
+ mSlotIndex = source.readInt();
+ mTransportType = source.readInt();
+ mApnType = source.readInt();
+ mThrottleExpiryTimeMillis = source.readLong();
+ mRetryType = source.readInt();
+ mThrottleType = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSlotIndex);
+ dest.writeInt(mTransportType);
+ dest.writeInt(mApnType);
+ dest.writeLong(mThrottleExpiryTimeMillis);
+ dest.writeInt(mRetryType);
+ dest.writeInt(mThrottleType);
+ }
+
+ public static final @NonNull Parcelable.Creator<ApnThrottleStatus> CREATOR =
+ new Parcelable.Creator<ApnThrottleStatus>() {
+ @Override
+ public ApnThrottleStatus createFromParcel(@NonNull Parcel source) {
+ return new ApnThrottleStatus(source);
+ }
+
+ @Override
+ public ApnThrottleStatus[] newArray(int size) {
+ return new ApnThrottleStatus[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType,
+ mThrottleExpiryTimeMillis, mTransportType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (obj instanceof ApnThrottleStatus) {
+ ApnThrottleStatus other = (ApnThrottleStatus) obj;
+ return this.mSlotIndex == other.mSlotIndex
+ && this.mApnType == other.mApnType
+ && this.mRetryType == other.mRetryType
+ && this.mThrottleType == other.mThrottleType
+ && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis
+ && this.mTransportType == other.mTransportType;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ApnThrottleStatus{"
+ + "mSlotIndex=" + mSlotIndex
+ + ", mTransportType=" + mTransportType
+ + ", mApnType=" + ApnSetting.getApnTypeString(mApnType)
+ + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis
+ + ", mRetryType=" + mRetryType
+ + ", mThrottleType=" + mThrottleType
+ + '}';
+ }
+
+ /**
+ * Provides a convenient way to set the fields of an {@link ApnThrottleStatus} when creating a
+ * new instance.
+ *
+ * <p>The example below shows how you might create a new {@code ApnThrottleStatus}:
+ *
+ * <pre><code>
+ *
+ * DataCallResponseApnThrottleStatus = new ApnThrottleStatus.Builder()
+ * .setSlotIndex(1)
+ * .setApnType({@link ApnSetting#TYPE_EMERGENCY})
+ * .setNoThrottle()
+ * .setRetryType({@link ApnThrottleStatus#RETRY_TYPE_NEW_CONNECTION})
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+ private int mSlotIndex;
+ private @AccessNetworkConstants.TransportType int mTransportType;
+ private @Annotation.ApnType int mApnType;
+ private long mThrottleExpiryTimeMillis;
+ private @RetryType int mRetryType;
+ private @ThrottleType int mThrottleType;
+ public static final long NO_THROTTLE_EXPIRY_TIME =
+ DataCallResponse.RETRY_DURATION_UNDEFINED;
+
+ /**
+ * Default constructor for the Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Set the slot index.
+ *
+ * @param slotIndex the slot index.
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setSlotIndex(int slotIndex) {
+ this.mSlotIndex = slotIndex;
+ return this;
+ }
+
+ /**
+ * Set the transport type.
+ *
+ * @param transportType the transport type.
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setTransportType(@AccessNetworkConstants.TransportType
+ int transportType) {
+ this.mTransportType = transportType;
+ return this;
+ }
+
+ /**
+ * Set the APN type.
+ *
+ * @param apnType the APN type.
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setApnType(@Annotation.ApnType int apnType) {
+ this.mApnType = apnType;
+ return this;
+ }
+
+ /**
+ * Sets the time at which the throttle will expire. The value is based off of
+ * {@link SystemClock#elapsedRealtime}.
+ *
+ * When setting this value, the throttle type is set to
+ * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+ *
+ * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+ *
+ * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires.
+ * Throws {@link IllegalArgumentException} for values less
+ * than 0.
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setThrottleExpiryTimeMillis(
+ @ElapsedRealtimeLong long throttleExpiryTimeMillis) {
+ if (throttleExpiryTimeMillis >= 0) {
+ this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+ this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME;
+ } else {
+ throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than "
+ + "or equal to 0");
+ }
+ return this;
+ }
+
+ /**
+ * Sets the status of the APN type as not being throttled.
+ *
+ * When setting this value, the throttle type is set to
+ * {@link ApnThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to
+ * {@link Builder#NO_THROTTLE_EXPIRY_TIME}.
+ *
+ * @return The same instance of the builder.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setNoThrottle() {
+ mThrottleType = THROTTLE_TYPE_NONE;
+ mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME;
+ return this;
+ }
+
+ /**
+ * Set the type of request that the framework will make the next time it retries
+ * to call {@link IDataService#setupDataCall}.
+ *
+ * @param retryType the type of request
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setRetryType(@RetryType int retryType) {
+ this.mRetryType = retryType;
+ return this;
+ }
+
+ /**
+ * Build the {@link ApnThrottleStatus}
+ *
+ * @return the {@link ApnThrottleStatus} object
+ */
+ @NonNull
+ public ApnThrottleStatus build() {
+ return new ApnThrottleStatus(
+ mSlotIndex,
+ mTransportType,
+ mApnType,
+ mThrottleType,
+ mThrottleExpiryTimeMillis,
+ mRetryType);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index aae77135cc58..623c4e20b3e1 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -135,6 +135,8 @@ public final class DataCallResponse implements Parcelable {
private final int mMtuV6;
private final @HandoverFailureMode int mHandoverFailureMode;
private final int mPduSessionId;
+ private final Qos mDefaultQos;
+ private final List<QosSession> mQosSessions;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -183,6 +185,8 @@ public final class DataCallResponse implements Parcelable {
mMtu = mMtuV4 = mMtuV6 = mtu;
mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
mPduSessionId = PDU_SESSION_ID_NOT_SET;
+ mDefaultQos = null;
+ mQosSessions = new ArrayList<>();
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -190,7 +194,8 @@ public final class DataCallResponse implements Parcelable {
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
- @HandoverFailureMode int handoverFailureMode, int pduSessionId) {
+ @HandoverFailureMode int handoverFailureMode, int pduSessionId,
+ @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -210,6 +215,8 @@ public final class DataCallResponse implements Parcelable {
mMtuV6 = mtuV6;
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
+ mDefaultQos = defaultQos;
+ mQosSessions = qosSessions;
}
/** @hide */
@@ -234,6 +241,9 @@ public final class DataCallResponse implements Parcelable {
mMtuV6 = source.readInt();
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
+ mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
+ mQosSessions = new ArrayList<>();
+ source.readList(mQosSessions, QosSession.class.getClassLoader());
}
/**
@@ -357,6 +367,28 @@ public final class DataCallResponse implements Parcelable {
return mPduSessionId;
}
+ /**
+ * @return default QOS of the data call received from the network
+ *
+ * @hide
+ */
+
+ @Nullable
+ public Qos getDefaultQos() {
+ return mDefaultQos;
+ }
+
+ /**
+ * @return All the dedicated bearer QOS sessions of the data call received from the network
+ *
+ * @hide
+ */
+
+ @NonNull
+ public List<QosSession> getQosSessions() {
+ return mQosSessions;
+ }
+
@NonNull
@Override
public String toString() {
@@ -377,6 +409,8 @@ public final class DataCallResponse implements Parcelable {
.append(" mtuV6=").append(getMtuV6())
.append(" handoverFailureMode=").append(getHandoverFailureMode())
.append(" pduSessionId=").append(getPduSessionId())
+ .append(" defaultQos=").append(mDefaultQos)
+ .append(" qosSessions=").append(mQosSessions)
.append("}");
return sb.toString();
}
@@ -390,12 +424,22 @@ public final class DataCallResponse implements Parcelable {
}
DataCallResponse other = (DataCallResponse) o;
- return this.mCause == other.mCause
- && this.mSuggestedRetryTime == other.mSuggestedRetryTime
- && this.mId == other.mId
- && this.mLinkStatus == other.mLinkStatus
- && this.mProtocolType == other.mProtocolType
- && this.mInterfaceName.equals(other.mInterfaceName)
+
+ final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ?
+ mDefaultQos == other.mDefaultQos :
+ mDefaultQos.equals(other.mDefaultQos);
+
+ final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
+ mQosSessions == other.mQosSessions :
+ mQosSessions.size() == other.mQosSessions.size()
+ && mQosSessions.containsAll(other.mQosSessions);
+
+ return mCause == other.mCause
+ && mSuggestedRetryTime == other.mSuggestedRetryTime
+ && mId == other.mId
+ && mLinkStatus == other.mLinkStatus
+ && mProtocolType == other.mProtocolType
+ && mInterfaceName.equals(other.mInterfaceName)
&& mAddresses.size() == other.mAddresses.size()
&& mAddresses.containsAll(other.mAddresses)
&& mDnsAddresses.size() == other.mDnsAddresses.size()
@@ -408,14 +452,17 @@ public final class DataCallResponse implements Parcelable {
&& mMtuV4 == other.mMtuV4
&& mMtuV6 == other.mMtuV6
&& mHandoverFailureMode == other.mHandoverFailureMode
- && mPduSessionId == other.mPduSessionId;
+ && mPduSessionId == other.mPduSessionId
+ && isQosSame
+ && isQosSessionsSame;
}
@Override
public int hashCode() {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+ mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
+ mQosSessions);
}
@Override
@@ -440,6 +487,12 @@ public final class DataCallResponse implements Parcelable {
dest.writeInt(mMtuV6);
dest.writeInt(mHandoverFailureMode);
dest.writeInt(mPduSessionId);
+ if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) {
+ dest.writeParcelable((EpsQos)mDefaultQos, flags);
+ } else {
+ dest.writeParcelable((NrQos)mDefaultQos, flags);
+ }
+ dest.writeList(mQosSessions);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -519,6 +572,10 @@ public final class DataCallResponse implements Parcelable {
private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
+ private Qos mDefaultQos;
+
+ private List<QosSession> mQosSessions = new ArrayList<>();
+
/**
* Default constructor for Builder.
*/
@@ -713,6 +770,35 @@ public final class DataCallResponse implements Parcelable {
}
/**
+ * Set the default QOS for this data connection.
+ *
+ * @param defaultQos QOS (Quality Of Service) received from network.
+ *
+ * @return The same instance of the builder.
+ *
+ * @hide
+ */
+ public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) {
+ mDefaultQos = defaultQos;
+ return this;
+ }
+
+ /**
+ * Set the dedicated bearer QOS sessions for this data connection.
+ *
+ * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+ * from network.
+ *
+ * @return The same instance of the builder.
+ *
+ * @hide
+ */
+ public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
+ mQosSessions = qosSessions;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -720,7 +806,8 @@ public final class DataCallResponse implements Parcelable {
public @NonNull DataCallResponse build() {
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId);
+ mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+ mDefaultQos, mQosSessions);
}
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 77685971c138..2ec965101930 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -107,6 +107,9 @@ public abstract class DataService extends Service {
private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11;
private static final int DATA_SERVICE_REQUEST_START_HANDOVER = 12;
private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER = 13;
+ private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED = 14;
+ private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED = 15;
+ private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED = 16;
private final HandlerThread mHandlerThread;
@@ -129,6 +132,8 @@ public abstract class DataService extends Service {
private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+ private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>();
+
/**
* Constructor
* @param slotIndex SIM slot index the data service provider associated with.
@@ -326,6 +331,19 @@ public abstract class DataService extends Service {
}
}
+ private void registerForApnUnthrottled(IDataServiceCallback callback) {
+ synchronized (mApnUnthrottledCallbacks) {
+ mApnUnthrottledCallbacks.add(callback);
+ }
+ }
+
+ private void unregisterForApnUnthrottled(IDataServiceCallback callback) {
+ synchronized (mApnUnthrottledCallbacks) {
+ mApnUnthrottledCallbacks.remove(callback);
+ }
+ }
+
+
/**
* Notify the system that current data call list changed. Data service must invoke this
* method whenever there is any data call status changed.
@@ -343,6 +361,21 @@ public abstract class DataService extends Service {
}
/**
+ * Notify the system that a given APN was unthrottled.
+ *
+ * @param apn Access Point Name defined by the carrier.
+ */
+ public final void notifyApnUnthrottled(@NonNull String apn) {
+ synchronized (mApnUnthrottledCallbacks) {
+ for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+ mSlotIndex, 0, new ApnUnthrottledIndication(apn,
+ callback)).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Called when the instance of data service is destroyed (e.g. got unbind or binder died)
* or when the data service provider is removed. The extended class should implement this
* method to perform cleanup works.
@@ -429,6 +462,16 @@ public abstract class DataService extends Service {
}
}
+ private static final class ApnUnthrottledIndication {
+ public final String apn;
+ public final IDataServiceCallback callback;
+ ApnUnthrottledIndication(String apn,
+ IDataServiceCallback callback) {
+ this.apn = apn;
+ this.callback = callback;
+ }
+ }
+
private class DataServiceHandler extends Handler {
DataServiceHandler(Looper looper) {
@@ -544,6 +587,26 @@ public abstract class DataService extends Service {
(cReq.callback != null)
? new DataServiceCallback(cReq.callback) : null);
break;
+ case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED:
+ if (serviceProvider == null) break;
+ serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj);
+ break;
+ case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED:
+ if (serviceProvider == null) break;
+ callback = (IDataServiceCallback) message.obj;
+ serviceProvider.unregisterForApnUnthrottled(callback);
+ break;
+ case DATA_SERVICE_INDICATION_APN_UNTHROTTLED:
+ if (serviceProvider == null) break;
+ ApnUnthrottledIndication apnUnthrottledIndication =
+ (ApnUnthrottledIndication) message.obj;
+ try {
+ apnUnthrottledIndication.callback
+ .onApnUnthrottled(apnUnthrottledIndication.apn);
+ } catch (RemoteException e) {
+ loge("Failed to call onApnUnthrottled. " + e);
+ }
+ break;
}
}
}
@@ -695,6 +758,26 @@ public abstract class DataService extends Service {
mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER,
slotIndex, 0, req).sendToTarget();
}
+
+ @Override
+ public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("registerForUnthrottleApn: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex,
+ 0, callback).sendToTarget();
+ }
+
+ @Override
+ public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+ if (callback == null) {
+ loge("uregisterForUnthrottleApn: callback is null");
+ return;
+ }
+ mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED,
+ slotIndex, 0, callback).sendToTarget();
+ }
}
private void log(String s) {
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index eef0e017f998..52bf15fd16c3 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -233,7 +233,7 @@ public class DataServiceCallback {
*/
@NonNull
public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) {
- switch(resultCode) {
+ switch (resultCode) {
case RESULT_SUCCESS:
return "RESULT_SUCCESS";
case RESULT_ERROR_UNSUPPORTED:
@@ -248,4 +248,22 @@ public class DataServiceCallback {
return "Missing case for result code=" + resultCode;
}
}
+
+ /**
+ * Indicates that the specified APN is no longer throttled.
+ *
+ * @param apn Access Point Name defined by the carrier.
+ */
+ public void onApnUnthrottled(@NonNull String apn) {
+ if (mCallback != null) {
+ try {
+ if (DBG) Rlog.d(TAG, "onApnUnthrottled");
+ mCallback.onApnUnthrottled(apn);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "onApnUnthrottled: remote exception", e);
+ }
+ } else {
+ Rlog.e(TAG, "onApnUnthrottled: callback is null!");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
new file mode 100644
index 000000000000..ad43068b2f11
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class EpsQos extends Qos implements Parcelable {
+
+ int qosClassId;
+
+ public EpsQos() {
+ super(Qos.QOS_TYPE_EPS,
+ new android.hardware.radio.V1_6.QosBandwidth(),
+ new android.hardware.radio.V1_6.QosBandwidth());
+ }
+
+ public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) {
+ super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink);
+ qosClassId = qos.qci;
+ }
+
+ private EpsQos(Parcel source) {
+ super(source);
+ qosClassId = source.readInt();
+ }
+
+ public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
+ return new EpsQos(in);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags);
+ dest.writeInt(qosClassId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), qosClassId);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof EpsQos)) {
+ return false;
+ }
+
+ EpsQos other = (EpsQos) o;
+
+ return this.qosClassId == other.qosClassId
+ && super.equals(other);
+ }
+
+ @Override
+ public String toString() {
+ return "EpsQos {"
+ + " qosClassId=" + qosClassId
+ + " downlink=" + downlink
+ + " uplink=" + uplink + "}";
+ }
+
+ public static final @NonNull Parcelable.Creator<EpsQos> CREATOR =
+ new Parcelable.Creator<EpsQos>() {
+ @Override
+ public EpsQos createFromParcel(Parcel source) {
+ return new EpsQos(source);
+ }
+
+ @Override
+ public EpsQos[] newArray(int size) {
+ return new EpsQos[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 33226feb0e35..3f1f033d6f11 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -40,4 +40,6 @@ oneway interface IDataService
void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback);
void startHandover(int slotId, int cid, IDataServiceCallback callback);
void cancelHandover(int slotId, int cid, IDataServiceCallback callback);
+ void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
+ void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback);
}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index d296e7b19be8..9cc2feac331a 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -32,4 +32,5 @@ oneway interface IDataServiceCallback
void onDataCallListChanged(in List<DataCallResponse> dataCallList);
void onHandoverStarted(int result);
void onHandoverCancelled(int result);
+ void onApnUnthrottled(in String apn);
}
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
index 3bf09bc19788..2904082616e7 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
@@ -17,6 +17,7 @@
package android.telephony.data;
import android.telephony.data.IQualifiedNetworksServiceCallback;
+import android.telephony.data.ApnThrottleStatus;
/**
* {@hide}
@@ -25,4 +26,5 @@ interface IQualifiedNetworksService
{
oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback);
oneway void removeNetworkAvailabilityProvider(int slotId);
+ oneway void reportApnThrottleStatusChanged(int slotId, in List<ApnThrottleStatus> statuses);
}
diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java
new file mode 100644
index 000000000000..2011eed26977
--- /dev/null
+++ b/telephony/java/android/telephony/data/NrQos.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to NR QOS.
+ *
+ * @hide
+ */
+public final class NrQos extends Qos implements Parcelable {
+ int qosFlowId;
+ int fiveQi;
+ int averagingWindowMs;
+
+ public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) {
+ super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink);
+ fiveQi = qos.fiveQi;
+ qosFlowId = qos.qfi;
+ averagingWindowMs = qos.averagingWindowMs;
+ }
+
+ private NrQos(Parcel source) {
+ super(source);
+ this.qosFlowId = source.readInt();
+ this.fiveQi = source.readInt();
+ this.averagingWindowMs = source.readInt();
+ }
+
+ public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) {
+ return new NrQos(in);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags);
+ dest.writeInt(qosFlowId);
+ dest.writeInt(fiveQi);
+ dest.writeInt(averagingWindowMs);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof NrQos)) {
+ return false;
+ }
+
+ NrQos other = (NrQos) o;
+
+ if (!super.equals(other)) {
+ return false;
+ }
+
+ return this.qosFlowId == other.qosFlowId
+ && this.fiveQi == other.fiveQi
+ && this.averagingWindowMs == other.averagingWindowMs;
+ }
+
+ @Override
+ public String toString() {
+ return "NrQos {"
+ + " fiveQi=" + fiveQi
+ + " downlink=" + downlink
+ + " uplink=" + uplink
+ + " qosFlowId=" + qosFlowId
+ + " averagingWindowMs=" + averagingWindowMs + "}";
+ }
+
+ public static final @NonNull Parcelable.Creator<NrQos> CREATOR =
+ new Parcelable.Creator<NrQos>() {
+ @Override
+ public NrQos createFromParcel(Parcel source) {
+ return new NrQos(source);
+ }
+
+ @Override
+ public NrQos[] newArray(int size) {
+ return new NrQos[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
new file mode 100644
index 000000000000..c8bb91e28bf2
--- /dev/null
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -0,0 +1,175 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class that stores information specific to QOS.
+ *
+ * @hide
+ */
+public abstract class Qos {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "QOS_TYPE_",
+ value = {QOS_TYPE_EPS, QOS_TYPE_NR})
+ public @interface QosType {}
+
+ @QosType
+ final int type;
+
+ static final int QOS_TYPE_EPS = 1;
+ static final int QOS_TYPE_NR = 2;
+
+ final QosBandwidth downlink;
+ final QosBandwidth uplink;
+
+ Qos(int type,
+ @NonNull android.hardware.radio.V1_6.QosBandwidth downlink,
+ @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) {
+ this.type = type;
+ this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps);
+ this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
+ }
+
+ static class QosBandwidth implements Parcelable {
+ int maxBitrateKbps;
+ int guaranteedBitrateKbps;
+
+ QosBandwidth() {
+ }
+
+ QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) {
+ this.maxBitrateKbps = maxBitrateKbps;
+ this.guaranteedBitrateKbps = guaranteedBitrateKbps;
+ }
+
+ private QosBandwidth(Parcel source) {
+ maxBitrateKbps = source.readInt();
+ guaranteedBitrateKbps = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(maxBitrateKbps);
+ dest.writeInt(guaranteedBitrateKbps);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosBandwidth)) {
+ return false;
+ }
+
+ QosBandwidth other = (QosBandwidth) o;
+ return maxBitrateKbps == other.maxBitrateKbps
+ && guaranteedBitrateKbps == other.guaranteedBitrateKbps;
+ }
+
+ @Override
+ public String toString() {
+ return "Bandwidth {"
+ + " maxBitrateKbps=" + maxBitrateKbps
+ + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}";
+ }
+
+ public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR =
+ new Parcelable.Creator<QosBandwidth>() {
+ @Override
+ public QosBandwidth createFromParcel(Parcel source) {
+ return new QosBandwidth(source);
+ }
+
+ @Override
+ public QosBandwidth[] newArray(int size) {
+ return new QosBandwidth[size];
+ }
+ };
+ };
+
+ protected Qos(@NonNull Parcel source) {
+ type = source.readInt();
+ downlink = source.readParcelable(QosBandwidth.class.getClassLoader());
+ uplink = source.readParcelable(QosBandwidth.class.getClassLoader());
+ }
+
+ /**
+ * Used by child classes for parceling.
+ *
+ * @hide
+ */
+ @CallSuper
+ public void writeToParcel(@QosType int type, Parcel dest, int flags) {
+ dest.writeInt(type);
+ dest.writeParcelable(downlink, flags);
+ dest.writeParcelable(uplink, flags);
+ }
+
+ /** @hide */
+ public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) {
+ switch (qos.getDiscriminator()) {
+ case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps:
+ return new EpsQos(qos.eps());
+ case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr:
+ return new NrQos(qos.nr());
+ default:
+ return null;
+ }
+ }
+
+ /** @hide */
+ public @QosType int getType() {
+ return type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(downlink, uplink);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ Qos other = (Qos) o;
+ return type == other.type
+ && downlink.equals(other.downlink)
+ && uplink.equals(other.uplink);
+ }
+}
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosFilter.java
new file mode 100644
index 000000000000..69277445634d
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosFilter.java
@@ -0,0 +1,373 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores QOS filter parameters as defined in
+ * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13.
+ *
+ * @hide
+ */
+public final class QosFilter implements Parcelable {
+
+ private List<LinkAddress> localAddresses;
+ private List<LinkAddress> remoteAddresses;
+ private PortRange localPort;
+ private PortRange remotePort;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "QOS_PROTOCOL_",
+ value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP,
+ QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH})
+ public @interface QosProtocol {}
+
+ public static final int QOS_PROTOCOL_UNSPECIFIED =
+ android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED;
+ public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP;
+ public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP;
+ public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP;
+ public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH;
+
+ @QosProtocol
+ private int protocol;
+
+ private int typeOfServiceMask;
+
+ private long flowLabel;
+
+ /** IPSec security parameter index */
+ private long securityParameterIndex;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "QOS_FILTER_DIRECTION_",
+ value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
+ QOS_FILTER_DIRECTION_BIDIRECTIONAL})
+ public @interface QosFilterDirection {}
+
+ public static final int QOS_FILTER_DIRECTION_DOWNLINK =
+ android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
+ public static final int QOS_FILTER_DIRECTION_UPLINK =
+ android.hardware.radio.V1_6.QosFilterDirection.UPLINK;
+ public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
+ android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
+
+ @QosFilterDirection
+ private int filterDirection;
+
+ /**
+ * Specified the order in which the filter needs to be matched.
+ * A Lower numerical value has a higher precedence.
+ */
+ private int precedence;
+
+ QosFilter() {
+ localAddresses = new ArrayList<>();
+ remoteAddresses = new ArrayList<>();
+ localPort = new PortRange();
+ remotePort = new PortRange();
+ protocol = QOS_PROTOCOL_UNSPECIFIED;
+ filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
+ }
+
+ public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+ PortRange localPort, PortRange remotePort, int protocol, int tos,
+ long flowLabel, long spi, int direction, int precedence) {
+ this.localAddresses = localAddresses;
+ this.remoteAddresses = remoteAddresses;
+ this.localPort = localPort;
+ this.remotePort = remotePort;
+ this.protocol = protocol;
+ this.typeOfServiceMask = tos;
+ this.flowLabel = flowLabel;
+ this.securityParameterIndex = spi;
+ this.filterDirection = direction;
+ this.precedence = precedence;
+ }
+
+ /** @hide */
+ public static @NonNull QosFilter create(
+ @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
+ QosFilter ret = new QosFilter();
+
+ String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
+ if (localAddresses != null) {
+ for (String address : localAddresses) {
+ ret.localAddresses.add(createLinkAddressFromString(address));
+ }
+ }
+
+ String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new);
+ if (remoteAddresses != null) {
+ for (String address : remoteAddresses) {
+ ret.remoteAddresses.add(createLinkAddressFromString(address));
+ }
+ }
+
+ if (qosFilter.localPort != null) {
+ if (qosFilter.localPort.getDiscriminator()
+ == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
+ final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range();
+ ret.localPort.start = portRange.start;
+ ret.localPort.end = portRange.end;
+ }
+ }
+
+ if (qosFilter.remotePort != null) {
+ if (qosFilter.remotePort.getDiscriminator()
+ == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) {
+ final android.hardware.radio.V1_6.PortRange portRange
+ = qosFilter.remotePort.range();
+ ret.remotePort.start = portRange.start;
+ ret.remotePort.end = portRange.end;
+ }
+ }
+
+ ret.protocol = qosFilter.protocol;
+
+ if (qosFilter.tos != null) {
+ if (qosFilter.tos.getDiscriminator()
+ == android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) {
+ ret.typeOfServiceMask = qosFilter.tos.value();
+ }
+ }
+
+ if (qosFilter.flowLabel != null) {
+ if (qosFilter.flowLabel.getDiscriminator()
+ == android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) {
+ ret.flowLabel = qosFilter.flowLabel.value();
+ }
+ }
+
+ if (qosFilter.spi != null) {
+ if (qosFilter.spi.getDiscriminator()
+ == android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) {
+ ret.securityParameterIndex = qosFilter.spi.value();
+ }
+ }
+
+ ret.filterDirection = qosFilter.direction;
+ ret.precedence = qosFilter.precedence;
+
+ return ret;
+ }
+
+ public static class PortRange implements Parcelable {
+ int start;
+ int end;
+
+ PortRange() {
+ start = -1;
+ end = -1;
+ }
+
+ private PortRange(Parcel source) {
+ start = source.readInt();
+ end = source.readInt();
+ }
+
+ public PortRange(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(start);
+ dest.writeInt(end);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<PortRange> CREATOR =
+ new Parcelable.Creator<PortRange>() {
+ @Override
+ public PortRange createFromParcel(Parcel source) {
+ return new PortRange(source);
+ }
+
+ @Override
+ public PortRange[] newArray(int size) {
+ return new PortRange[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "PortRange {"
+ + " start=" + start
+ + " end=" + end + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof PortRange)) {
+ return false;
+ }
+
+ PortRange other = (PortRange) o;
+ return start == other.start
+ && end == other.end;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(start, end);
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "QosFilter {"
+ + " localAddresses=" + localAddresses
+ + " remoteAddresses=" + remoteAddresses
+ + " localPort=" + localPort
+ + " remotePort=" + remotePort
+ + " protocol=" + protocol
+ + " typeOfServiceMask=" + typeOfServiceMask
+ + " flowLabel=" + flowLabel
+ + " securityParameterIndex=" + securityParameterIndex
+ + " filterDirection=" + filterDirection
+ + " precedence=" + precedence + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(localAddresses, remoteAddresses, localPort,
+ remotePort, protocol, typeOfServiceMask, flowLabel,
+ securityParameterIndex, filterDirection, precedence);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosFilter)) {
+ return false;
+ }
+
+ QosFilter other = (QosFilter) o;
+
+ return localAddresses.size() == other.localAddresses.size()
+ && localAddresses.containsAll(other.localAddresses)
+ && remoteAddresses.size() == other.remoteAddresses.size()
+ && remoteAddresses.containsAll(other.remoteAddresses)
+ && localPort.equals(other.localPort)
+ && remotePort.equals(other.remotePort)
+ && protocol == other.protocol
+ && typeOfServiceMask == other.typeOfServiceMask
+ && flowLabel == other.flowLabel
+ && securityParameterIndex == other.securityParameterIndex
+ && filterDirection == other.filterDirection
+ && precedence == other.precedence;
+ }
+
+ private static LinkAddress createLinkAddressFromString(String addressString) {
+ addressString = addressString.trim();
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = addressString.split("/", 2);
+ address = InetAddresses.parseNumericAddress(pieces[0]);
+ if (pieces.length == 1) {
+ prefixLength = (address instanceof Inet4Address) ? 32 : 128;
+ } else if (pieces.length == 2) {
+ prefixLength = Integer.parseInt(pieces[1]);
+ }
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid link address " + addressString);
+ }
+
+ return new LinkAddress(address, prefixLength, 0, 0,
+ LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
+ }
+
+ private QosFilter(Parcel source) {
+ localAddresses = new ArrayList<>();
+ source.readList(localAddresses, LinkAddress.class.getClassLoader());
+ remoteAddresses = new ArrayList<>();
+ source.readList(remoteAddresses, LinkAddress.class.getClassLoader());
+ localPort = source.readParcelable(PortRange.class.getClassLoader());
+ remotePort = source.readParcelable(PortRange.class.getClassLoader());
+ protocol = source.readInt();
+ typeOfServiceMask = source.readInt();
+ flowLabel = source.readLong();
+ securityParameterIndex = source.readLong();
+ filterDirection = source.readInt();
+ precedence = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeList(localAddresses);
+ dest.writeList(remoteAddresses);
+ dest.writeParcelable(localPort, flags);
+ dest.writeParcelable(remotePort, flags);
+ dest.writeInt(protocol);
+ dest.writeInt(typeOfServiceMask);
+ dest.writeLong(flowLabel);
+ dest.writeLong(securityParameterIndex);
+ dest.writeInt(filterDirection);
+ dest.writeInt(precedence);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
+ new Parcelable.Creator<QosFilter>() {
+ @Override
+ public QosFilter createFromParcel(Parcel source) {
+ return new QosFilter(source);
+ }
+
+ @Override
+ public QosFilter[] newArray(int size) {
+ return new QosFilter[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
new file mode 100644
index 000000000000..f07b6a9f6725
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosSession.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosSession implements Parcelable{
+
+ final int qosSessionId;
+ final Qos qos;
+ final List<QosFilter> qosFilterList;
+
+ public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
+ this.qosSessionId = qosSessionId;
+ this.qos = qos;
+ this.qosFilterList = qosFilterList;
+ }
+
+ private QosSession(Parcel source) {
+ qosSessionId = source.readInt();
+ qos = source.readParcelable(Qos.class.getClassLoader());
+ qosFilterList = new ArrayList<>();
+ source.readList(qosFilterList, QosFilter.class.getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(qosSessionId);
+ if (qos.getType() == Qos.QOS_TYPE_EPS) {
+ dest.writeParcelable((EpsQos)qos, flags);
+ } else {
+ dest.writeParcelable((NrQos)qos, flags);
+ }
+ dest.writeList(qosFilterList);
+ }
+
+ public static @NonNull QosSession create(
+ @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+ List<QosFilter> qosFilters = new ArrayList<>();
+
+ if (qosSession.qosFilters != null) {
+ for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+ qosFilters.add(QosFilter.create(filter));
+ }
+ }
+
+ return new QosSession(
+ qosSession.qosSessionId,
+ Qos.create(qosSession.qos),
+ qosFilters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "QosSession {"
+ + " qosSessionId=" + qosSessionId
+ + " qos=" + qos
+ + " qosFilterList=" + qosFilterList + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qosSessionId, qos, qosFilterList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosSession)) {
+ return false;
+ }
+
+ QosSession other = (QosSession) o;
+ return this.qosSessionId == other.qosSessionId
+ && this.qos.equals(other.qos)
+ && this.qosFilterList.size() == other.qosFilterList.size()
+ && this.qosFilterList.containsAll(other.qosFilterList);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
+ new Parcelable.Creator<QosSession>() {
+ @Override
+ public QosSession createFromParcel(Parcel source) {
+ return new QosSession(source);
+ }
+
+ @Override
+ public QosSession[] newArray(int size) {
+ return new QosSession[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 05971c4d2e70..4af63b4cf981 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -28,6 +28,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -65,6 +66,7 @@ public abstract class QualifiedNetworksService extends Service {
private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER = 2;
private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS = 3;
private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4;
+ private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5;
private final HandlerThread mHandlerThread;
@@ -160,6 +162,17 @@ public abstract class QualifiedNetworksService extends Service {
}
/**
+ * The framework calls this method when the throttle status of an APN changes.
+ *
+ * This method is meant to be overridden.
+ *
+ * @param statuses the statuses that have changed
+ */
+ public void reportApnThrottleStatusChanged(@NonNull List<ApnThrottleStatus> statuses) {
+ Log.d(TAG, "reportApnThrottleStatusChanged: statuses size=" + statuses.size());
+ }
+
+ /**
* Called when the qualified networks provider is removed. The extended class should
* implement this method to perform cleanup works.
*/
@@ -197,6 +210,12 @@ public abstract class QualifiedNetworksService extends Service {
+ slotIndex);
}
break;
+ case QNS_APN_THROTTLE_STATUS_CHANGED:
+ if (provider != null) {
+ List<ApnThrottleStatus> statuses = (List<ApnThrottleStatus>) message.obj;
+ provider.reportApnThrottleStatusChanged(statuses);
+ }
+ break;
case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER:
if (provider != null) {
@@ -286,6 +305,13 @@ public abstract class QualifiedNetworksService extends Service {
mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0)
.sendToTarget();
}
+
+ @Override
+ public void reportApnThrottleStatusChanged(int slotIndex,
+ List<ApnThrottleStatus> statuses) {
+ mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses)
+ .sendToTarget();
+ }
}
private void log(String s) {
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 76fc4f7d0519..6fbde503c3a0 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -113,6 +113,7 @@ public class DctConstants {
public static final int EVENT_NR_TIMER_WATCHDOG = BASE + 53;
public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54;
public static final int EVENT_SIM_STATE_UPDATED = BASE + 55;
+ public static final int EVENT_APN_UNTHROTTLED = BASE + 56;
/***** Constants *****/
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index dba1856ea6d0..70f6386aa891 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -200,7 +200,8 @@ class ConnectivityServiceIntegrationTest {
nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */,
+ context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
@@ -238,7 +239,7 @@ class ConnectivityServiceIntegrationTest {
val lp = LinkProperties()
lp.captivePortalApiUrl = Uri.parse(apiUrl)
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 85704d033634..2a24d1ac22d2 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -72,12 +72,12 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
private long mKeepaliveResponseDelay = 0L;
private Integer mExpectedKeepaliveSlot = null;
- public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
- throws Exception {
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate, Context context) throws Exception {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
- mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
index eb290dc7d24a..938a694e8ba9 100644
--- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -35,4 +35,5 @@ class TestNetIdManager : NetIdManager() {
private val nextId = AtomicInteger(MAX_NET_ID)
override fun reserveNetId() = nextId.decrementAndGet()
override fun releaseNetId(id: Int) = Unit
+ fun peekNextNetId() = nextId.get() - 1
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b78f0e237bc5..4bb13172335b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -322,6 +322,7 @@ public class ConnectivityServiceTest {
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -339,6 +340,7 @@ public class ConnectivityServiceTest {
private INetworkPolicyListener mPolicyListener;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
+ private TestNetIdManager mNetIdManager;
@Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -617,12 +619,17 @@ public class ConnectivityServiceTest {
private String mRedirectUrl;
TestNetworkAgentWrapper(int transport) throws Exception {
- this(transport, new LinkProperties());
+ this(transport, new LinkProperties(), null);
}
TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
throws Exception {
- super(transport, linkProperties, mServiceContext);
+ this(transport, linkProperties, null);
+ }
+
+ private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate) throws Exception {
+ super(transport, linkProperties, ncTemplate, mServiceContext);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -1017,46 +1024,36 @@ public class ConnectivityServiceTest {
}
}
+ private Set<UidRange> uidRangesForUid(int uid) {
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ return ranges;
+ }
+
private static Looper startHandlerThreadAndReturnLooper() {
final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
handlerThread.start();
return handlerThread.getLooper();
}
- private class MockVpn extends Vpn {
- // TODO : the interactions between this mock and the mock network agent are too
- // hard to get right at this moment, because it's unclear in which case which
- // target needs to get a method call or both, and in what order. It's because
- // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn
- // parent class of MockVpn agent wants that responsibility.
- // That being said inside the test it should be possible to make the interactions
- // harder to get wrong with precise speccing, judicious comments, helper methods
- // and a few sprinkled assertions.
-
- private boolean mConnected = false;
+ private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork {
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
- private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
+ private boolean mAgentRegistered = false;
+ private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
- private Network[] mUnderlyingNetworks;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId, mock(KeyStore.class));
- }
-
- public void setNetworkAgent(TestNetworkAgentWrapper agent) {
- agent.waitForIdle(TIMEOUT_MS);
- mMockNetworkAgent = agent;
- mNetworkAgent = agent.getNetworkAgent();
- mNetworkCapabilities.set(agent.getNetworkCapabilities());
+ mConfig = new VpnConfig();
}
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities(null /* defaultNetwork */);
+ updateCapabilitiesInternal(null /* defaultNetwork */, true);
}
public void setVpnType(int vpnType) {
@@ -1064,21 +1061,13 @@ public class ConnectivityServiceTest {
}
@Override
- public int getNetId() {
- if (mMockNetworkAgent == null) {
- return NETID_UNSET;
- }
- return mMockNetworkAgent.getNetwork().netId;
- }
-
- @Override
- public boolean appliesToUid(int uid) {
- return mConnected; // Trickery to simplify testing.
+ public Network getNetwork() {
+ return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
}
@Override
- protected boolean isCallerEstablishedOwnerLocked() {
- return mConnected; // Similar trickery
+ public int getNetId() {
+ return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
}
@Override
@@ -1086,41 +1075,94 @@ public class ConnectivityServiceTest {
return mVpnType;
}
- private void connect(boolean isAlwaysMetered) {
- mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
- mConnected = true;
- mConfig = new VpnConfig();
+ private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
+ throws Exception {
+ if (mAgentRegistered) throw new IllegalStateException("already registered");
+ setUids(uids);
mConfig.isMetered = isAlwaysMetered;
+ mInterface = VPN_IFNAME;
+ mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
+ mNetworkCapabilities);
+ mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
+ mAgentRegistered = true;
+ mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
+ mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
}
- public void connectAsAlwaysMetered() {
- connect(true /* isAlwaysMetered */);
+ private void registerAgent(Set<UidRange> uids) throws Exception {
+ registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
}
- public void connect() {
- connect(false /* isAlwaysMetered */);
+ private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
+ mMockNetworkAgent.connect(validated, hasInternet, isStrictMode);
}
- @Override
- public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
- if (!mConnected) return null;
+ private void connect(boolean validated) {
+ mMockNetworkAgent.connect(validated);
+ }
+
+ private TestNetworkAgentWrapper getAgent() {
+ return mMockNetworkAgent;
+ }
+
+ public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
+ boolean hasInternet, boolean isStrictMode) throws Exception {
+ mNetworkCapabilities.setOwnerUid(uid);
+ mNetworkCapabilities.setAdministratorUids(new int[]{uid});
+ registerAgent(false, ranges, lp);
+ connect(validated, hasInternet, isStrictMode);
+ waitForIdle();
+ }
+
+ public void establish(LinkProperties lp, int uid, Set<UidRange> ranges) throws Exception {
+ establish(lp, uid, ranges, true, true, false);
+ }
+
+ public void establishForMyUid(LinkProperties lp) throws Exception {
+ final int uid = Process.myUid();
+ establish(lp, uid, uidRangesForUid(uid), true, true, false);
+ }
+
+ public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
+ throws Exception {
+ final int uid = Process.myUid();
+ establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+ isStrictMode);
+ }
+
+ public void establishForMyUid() throws Exception {
+ establishForMyUid(new LinkProperties());
+ }
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mMockNetworkAgent.sendLinkProperties(lp);
+ }
+
+ private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork,
+ boolean sendToConnectivityService) {
+ if (!mAgentRegistered) return null;
super.updateCapabilities(defaultNetwork);
// Because super.updateCapabilities will update the capabilities of the agent but
// not the mock agent, the mock agent needs to know about them.
- copyCapabilitiesToNetworkAgent();
+ copyCapabilitiesToNetworkAgent(sendToConnectivityService);
return new NetworkCapabilities(mNetworkCapabilities);
}
- private void copyCapabilitiesToNetworkAgent() {
+ private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) {
if (null != mMockNetworkAgent) {
mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
- false /* sendToConnectivityService */);
+ sendToConnectivityService);
}
}
+ @Override
+ public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+ return updateCapabilitiesInternal(defaultNetwork, false);
+ }
+
public void disconnect() {
- mConnected = false;
- mConfig = null;
+ if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+ mAgentRegistered = false;
}
@Override
@@ -1133,18 +1175,6 @@ public class ConnectivityServiceTest {
private synchronized void setVpnInfo(VpnInfo vpnInfo) {
mVpnInfo = vpnInfo;
}
-
- @Override
- public synchronized Network[] getUnderlyingNetworks() {
- if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
-
- return super.getUnderlyingNetworks();
- }
-
- /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
- private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
- mUnderlyingNetworks = underlyingNetworks;
- }
}
private void mockVpn(int uid) {
@@ -1207,6 +1237,8 @@ public class ConnectivityServiceTest {
@Before
public void setUp() throws Exception {
+ mNetIdManager = new TestNetIdManager();
+
mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
@@ -1277,7 +1309,7 @@ public class ConnectivityServiceTest {
doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetIdManager).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
@@ -1335,6 +1367,9 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+ mMockVpn.disconnect();
+ waitForIdle();
+
FakeSettingsProvider.clearSettingsProvider();
mCsHandlerThread.quitSafely();
@@ -3218,20 +3253,12 @@ public class ConnectivityServiceTest {
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ mMockVpn.establishForMyUid();
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -4808,13 +4835,52 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(networkCallback);
}
+ private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
+ // Easier to implement than a proper "assertSameElements" method that also correctly deals
+ // with duplicates.
+ final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
+ assertEquals(msg, expected.length, actual.length);
+ Set expectedSet = new ArraySet<>(Arrays.asList(expected));
+ assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
+ // actual cannot have duplicates because it's the same length and has the same elements.
+ Set actualSet = new ArraySet<>(Arrays.asList(actual));
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+ Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+ ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
+ ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
+ any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
+
+ assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+
+ VpnInfo[] infos = vpnInfosCaptor.getValue();
+ if (vpnUid != null) {
+ assertEquals("Should have exactly one VPN:", 1, infos.length);
+ VpnInfo info = infos[0];
+ assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
+ assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ } else {
+ assertEquals(0, infos.length);
+ return;
+ }
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
+ expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+ }
+
@Test
public void testStatsIfacesChanged() throws Exception {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
- Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+ final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+ final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4825,9 +4891,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4835,32 +4899,24 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
- eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Captive portal change shouldn't update ifaces
@@ -4874,9 +4930,102 @@ public class ConnectivityServiceTest {
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
+ reset(mStatsService);
+
+ // Test VPNs.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+
+ mMockVpn.establishForMyUid(lp);
+
+ final Network[] cellAndVpn = new Network[] {
+ mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+ Network[] cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ // A VPN with default (null) underlying networks sets the underlying network's interfaces...
+ expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+
+ // ...and updates them as the default network switches.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ final Network[] wifiAndVpn = new Network[] {
+ mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+ cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ waitForIdle();
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{WIFI_IFNAME});
+ reset(mStatsService);
+
+ // A VPN that sets its underlying networks passes the underlying interfaces, and influences
+ // the default interface sent to NetworkStatsService by virtue of applying to the system
+ // server UID (or, in this test, to the test's UID). This is the reason for sending
+ // MOBILE_IFNAME even though the default network is wifi.
+ // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
+ // applies to the system server UID should not have any bearing on network stats.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+ reset(mStatsService);
+
+ mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
+ // If an underlying network disconnects, that interface should no longer be underlying.
+ // This doesn't actually work because disconnectAndDestroyNetwork only notifies
+ // NetworkStatsService before the underlying network is actually removed. So the underlying
+ // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This
+ // could result in incorrect data usage measurements if the interface used by the
+ // disconnected network is reused by a system component that does not register an agent for
+ // it (e.g., tethering).
+ mCellNetworkAgent.disconnect();
+ waitForIdle();
+ assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+
+ // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
+ // network for the VPN...
+ verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(infos -> infos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ verifyNoMoreInteractions(mStatsService);
+ reset(mStatsService);
+
+ // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
+ // called again, it does. For example, connect Ethernet, but with a low score, such that it
+ // does not become the default network.
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.adjustScore(-40);
+ mEthernetNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ mEthernetNetworkAgent.disconnect();
+ reset(mStatsService);
+
+ // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
+ // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
+ // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which
+ // is probably a performance improvement (though it's very unlikely that a VPN would declare
+ // no underlying networks).
+ // Also, for the same reason as above, the active interface passed in is null.
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
}
@@ -5232,6 +5381,58 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
+ // (and doing so is difficult without using reflection) but it's good to test that the code
+ // behaves approximately correctly.
+ mMockVpn.establishForMyUid(false, true, false);
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
+ // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and cannot
+ // pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
+ // before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+
+ mMockVpn.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
@@ -5265,42 +5466,38 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
+ final Set<UidRange> ranges = uidRangesForUid(uid);
+ mMockVpn.registerAgent(ranges);
+
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().getNetworkCapabilities()));
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.connect(false);
- mMockVpn.connect();
- mMockVpn.setUnderlyingNetworks(new Network[0]);
+ mMockVpn.connect(false);
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
- genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, nc -> null == nc.getUids());
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
- vpnNetworkAgent.setUids(ranges);
+ mMockVpn.setUids(ranges);
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
// TODO : The default network callback should actually get a LOST call here (also see the
// comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -5308,19 +5505,18 @@ public class ConnectivityServiceTest {
// can't currently update their UIDs without disconnecting, so this does not matter too
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
- vpnNetworkAgent.setUids(ranges);
- genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
mWiFiNetworkAgent.disconnect();
@@ -5330,13 +5526,13 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -5358,20 +5554,13 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5390,21 +5579,14 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
- defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5422,44 +5604,36 @@ public class ConnectivityServiceTest {
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
- callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
callback.assertNoCallback();
- assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
- assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore());
+ assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
- NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
- callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn);
callback.assertNoCallback();
- vpnNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@@ -5481,21 +5655,15 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+ vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS,
nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5517,18 +5685,11 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5545,7 +5706,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5559,7 +5720,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5569,7 +5730,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5577,27 +5738,27 @@ public class ConnectivityServiceTest {
// Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
// Add NOT_SUSPENDED again and observe VPN is no longer suspended.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5607,7 +5768,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5620,7 +5781,7 @@ public class ConnectivityServiceTest {
// Stop using WiFi. The VPN is suspended again.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5634,7 +5795,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5645,14 +5806,14 @@ public class ConnectivityServiceTest {
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
mCellNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5673,18 +5834,11 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5696,7 +5850,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5706,7 +5860,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5718,7 +5872,7 @@ public class ConnectivityServiceTest {
// Disconnect wifi too. Now we have no default network.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5761,18 +5915,10 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
// Connect VPN network. By default it is using current default network (Cell).
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be metered.
assertTrue(mCm.isActiveNetworkMetered());
@@ -5783,7 +5929,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
waitForIdle();
// VPN should still be the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be unmetered as it should now be using WiFi (new default).
assertFalse(mCm.isActiveNetworkMetered());
@@ -5801,7 +5947,6 @@ public class ConnectivityServiceTest {
// VPN without any underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -5822,18 +5967,10 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
@@ -5873,7 +6010,6 @@ public class ConnectivityServiceTest {
// VPN without underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -5888,17 +6024,11 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connectAsAlwaysMetered();
+ mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+ new LinkProperties());
+ mMockVpn.connect(true);
waitForIdle();
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
mService.setUnderlyingNetworksForVpn(null);
@@ -5922,7 +6052,7 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
}
@Test
@@ -6654,34 +6784,21 @@ public class ConnectivityServiceTest {
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
- // Set up a VPN network with a proxy
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setUids(ranges);
+ // Connect a VPN network with a proxy.
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
- waitForIdle();
-
- // Connect to VPN with proxy
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid(testLinkProperties);
// Test that the VPN network returns a proxy, and the WiFi does not.
- assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
// Test that the VPN network returns no proxy when it is set to null.
testLinkProperties.setHttpProxy(null);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+ mMockVpn.sendLinkProperties(testLinkProperties);
waitForIdle();
- assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertNull(mService.getProxyForNetwork(null));
// Set WiFi proxy and check that the vpn proxy is still null.
@@ -6692,7 +6809,7 @@ public class ConnectivityServiceTest {
// Disconnect from VPN and check that the active network, which is now the WiFi, has the
// correct proxy setting.
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
@@ -6707,7 +6824,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6717,7 +6834,7 @@ public class ConnectivityServiceTest {
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange));
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
// Disconnected VPN should have interface rules removed
@@ -6734,8 +6851,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6750,8 +6866,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6765,7 +6880,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6777,7 +6892,7 @@ public class ConnectivityServiceTest {
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
lp.setInterfaceName("tun1");
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN handover (switch to a new interface) should result in rules being updated (old rules
// removed first, then new rules added)
@@ -6790,7 +6905,7 @@ public class ConnectivityServiceTest {
lp = new LinkProperties();
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1"));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN not routing everything should no longer have interface filtering rules
verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
@@ -6801,7 +6916,7 @@ public class ConnectivityServiceTest {
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Back to routing all IPv6 traffic should have filtering rules
verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
@@ -6816,8 +6931,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
- Collections.singleton(vpnRange));
+ mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
@@ -6826,7 +6940,7 @@ public class ConnectivityServiceTest {
final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
- vpnNetworkAgent.setUids(newRanges);
+ mMockVpn.setUids(newRanges);
waitForIdle();
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
@@ -6967,7 +7081,7 @@ public class ConnectivityServiceTest {
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+ mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
mMockVpn.setVpnType(vpnType);
final VpnInfo vpnInfo = new VpnInfo();
@@ -7048,19 +7162,6 @@ public class ConnectivityServiceTest {
mService.getConnectionOwnerUid(getTestConnectionInfo());
}
- private TestNetworkAgentWrapper establishVpn(
- LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception {
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
- vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(vpnRange);
- vpnNetworkAgent.connect(true);
- waitForIdle();
- return vpnNetworkAgent;
- }
-
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
@@ -7240,22 +7341,28 @@ public class ConnectivityServiceTest {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
// setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
// active
final VpnInfo info = new VpnInfo();
info.ownerUid = Process.myUid();
- info.vpnIface = "interface";
+ info.vpnIface = VPN_IFNAME;
mMockVpn.setVpnInfo(info);
- mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+
+ mMockVpn.establishForMyUid();
+ waitForIdle();
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
assertTrue(
"Active VPN permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- mMockVpn.overrideUnderlyingNetworks(null);
+ assertTrue(mService.setUnderlyingNetworksForVpn(null));
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
mService.checkConnectivityDiagnosticsPermissions(
@@ -7276,8 +7383,6 @@ public class ConnectivityServiceTest {
Manifest.permission.ACCESS_FINE_LOCATION);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
- // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
- mMockVpn.disconnect();
assertTrue(
"NetworkCapabilities administrator uid permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index d235c80220ca..dc96df67e866 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -89,8 +89,6 @@ rule android.util.BackupUtils* com.android.wifi.x.@0
rule android.util.LocalLog* com.android.wifi.x.@0
rule android.util.Rational* com.android.wifi.x.@0
-rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0
-
# Use our statically linked bouncy castle library
rule org.bouncycastle.** com.android.wifi.x.@0
# Use our statically linked protobuf library