summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--cmds/bootanimation/BootAnimation.cpp2
-rw-r--r--core/api/current.txt35
-rw-r--r--core/api/system-current.txt26
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/accessibilityservice/MagnificationConfig.aidl (renamed from core/java/android/window/TaskFragmentAppearedInfo.aidl)8
-rw-r--r--core/java/android/accessibilityservice/MagnificationConfig.java250
-rw-r--r--core/java/android/app/ActivityManager.java28
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java31
-rw-r--r--core/java/android/content/Context.java36
-rw-r--r--core/java/android/content/Intent.java14
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java4
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java9
-rw-r--r--core/java/android/net/nsd/INsdManager.aidl13
-rw-r--r--core/java/android/net/nsd/INsdManagerCallback.aidl39
-rw-r--r--core/java/android/net/nsd/INsdServiceConnector.aidl35
-rw-r--r--core/java/android/net/nsd/NsdManager.java215
-rw-r--r--core/java/android/net/nsd/NsdServiceInfo.aidl19
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java9
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java56
-rw-r--r--core/java/android/nfc/cardemulation/NfcFCardEmulation.java1
-rw-r--r--core/java/android/nfc/cardemulation/NfcFServiceInfo.java7
-rw-r--r--core/java/android/os/IUserManager.aidl5
-rw-r--r--core/java/android/os/NewUserRequest.java142
-rw-r--r--core/java/android/os/UserManager.java69
-rw-r--r--core/java/android/provider/Settings.java19
-rw-r--r--core/java/android/provider/Telephony.java17
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java9
-rw-r--r--core/java/android/view/KeyEvent.java10
-rw-r--r--core/java/android/view/WindowManager.java20
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java91
-rw-r--r--core/java/android/window/DisplayWindowPolicyController.java84
-rw-r--r--core/java/android/window/ITaskFragmentOrganizer.aidl3
-rw-r--r--core/java/android/window/TaskFragmentAppearedInfo.java93
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java5
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java19
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--data/etc/com.android.intentresolver.xml1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java19
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java8
-rw-r--r--libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml19
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml6
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/size_compat_ui.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java31
-rw-r--r--libs/hwui/WebViewFunctorManager.cpp39
-rw-r--r--libs/hwui/WebViewFunctorManager.h1
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp1
-rw-r--r--libs/hwui/private/hwui/DrawVkInfo.h3
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp2
-rw-r--r--libs/input/PointerController.cpp90
-rw-r--r--libs/input/PointerController.h18
-rw-r--r--media/Android.bp1
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppService.aidl3
-rw-r--r--media/java/android/media/tv/interactive/TvIAppService.java18
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java18
-rw-r--r--media/jni/android_media_tv_Tuner.cpp18
-rw-r--r--media/jni/tuner/FilterClient.cpp8
-rw-r--r--media/jni/tuner/FilterClient.h8
-rw-r--r--omapi/aidl/Android.bp35
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl46
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl40
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl44
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl45
-rw-r--r--omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl48
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementChannel.aidl (renamed from core/java/android/se/omapi/ISecureElementChannel.aidl)4
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementListener.aidl (renamed from core/java/android/se/omapi/ISecureElementListener.aidl)1
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementReader.aidl (renamed from core/java/android/se/omapi/ISecureElementReader.aidl)3
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementService.aidl (renamed from core/java/android/se/omapi/ISecureElementService.aidl)12
-rw-r--r--omapi/aidl/android/se/omapi/ISecureElementSession.aidl (renamed from core/java/android/se/omapi/ISecureElementSession.aidl)10
-rw-r--r--omapi/aidl/vts/functional/AccessControlApp/Android.bp54
-rw-r--r--omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp428
-rw-r--r--omapi/aidl/vts/functional/omapi/Android.bp54
-rw-r--r--omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp609
-rw-r--r--omapi/java/Android.bp17
-rw-r--r--omapi/java/android/se/OWNERS (renamed from core/java/android/se/OWNERS)0
-rw-r--r--omapi/java/android/se/omapi/Channel.java (renamed from core/java/android/se/omapi/Channel.java)0
-rw-r--r--omapi/java/android/se/omapi/OWNERS (renamed from core/java/android/se/omapi/OWNERS)0
-rw-r--r--omapi/java/android/se/omapi/Reader.java (renamed from core/java/android/se/omapi/Reader.java)4
-rw-r--r--omapi/java/android/se/omapi/SEService.java (renamed from core/java/android/se/omapi/SEService.java)20
-rw-r--r--omapi/java/android/se/omapi/Session.java (renamed from core/java/android/se/omapi/Session.java)0
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java11
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt10
-rw-r--r--packages/SystemUI/res/layout-land-television/volume_dialog_row.xml2
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml11
-rw-r--r--packages/SystemUI/res/values-night/styles.xml8
-rw-r--r--packages/SystemUI/res/values/styles.xml6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java38
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java161
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java246
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java178
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java270
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java65
-rw-r--r--services/core/java/com/android/server/NsdService.java443
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java115
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java1
-rw-r--r--services/core/java/com/android/server/am/TraceErrorLogger.java13
-rw-r--r--services/core/java/com/android/server/am/UserController.java35
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java40
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerService.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/Installer.java54
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java21
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java33
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java72
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java34
-rw-r--r--services/core/java/com/android/server/power/Notifier.java6
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java16
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java186
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java118
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java3
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java173
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java23
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java8
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java16
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java3
-rw-r--r--services/core/java/com/android/server/wm/PackageConfigPersister.java43
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java7
-rw-r--r--services/core/java/com/android/server/wm/Transition.java52
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java5
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java12
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp355
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java19
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt32
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/AndroidTest.xml11
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java350
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java129
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java296
-rw-r--r--services/tests/servicestests/test-apps/StubApp/Android.bp37
-rw-r--r--services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml25
-rw-r--r--services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java22
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java12
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java65
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java22
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java4
-rw-r--r--telephony/common/Android.bp5
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java5
-rw-r--r--telephony/java/android/telephony/Annotation.java57
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java2
-rw-r--r--telephony/java/android/telephony/DataFailCause.java7
-rw-r--r--telephony/java/android/telephony/SignalStrengthUpdateRequest.java15
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java3
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java9
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java84
-rw-r--r--telephony/java/android/telephony/ims/DelegateRegistrationState.java34
235 files changed, 7297 insertions, 1711 deletions
diff --git a/Android.bp b/Android.bp
index d52f08f89038..731b9f617158 100644
--- a/Android.bp
+++ b/Android.bp
@@ -82,6 +82,7 @@ filegroup {
":framework-mca-filterpacks-sources",
":framework-media-sources",
":framework-mms-sources",
+ ":framework-omapi-sources",
":framework-opengl-sources",
":framework-rs-sources",
":framework-sax-sources",
@@ -273,6 +274,7 @@ java_library {
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
"android.hardware.vibrator-V2-java",
+ "android.se.omapi-V1-java",
"android.system.suspend.control.internal-java",
"devicepolicyprotosnano",
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 59f602c11247..af4053fb6c8c 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1576,7 +1576,7 @@ bool BootAnimation::playAnimation(const Animation& animation) {
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
- } while (err<0 && errno == EINTR);
+ } while (err == EINTR);
}
checkExit();
diff --git a/core/api/current.txt b/core/api/current.txt
index b9ade0b442ba..32865ad929e9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3255,6 +3255,28 @@ package android.accessibilityservice {
method public boolean willContinue();
}
+ public final class MagnificationConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public float getCenterX();
+ method public float getCenterY();
+ method public int getMode();
+ method public float getScale();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.MagnificationConfig> CREATOR;
+ field public static final int DEFAULT_MODE = 0; // 0x0
+ field public static final int FULLSCREEN_MODE = 1; // 0x1
+ field public static final int WINDOW_MODE = 2; // 0x2
+ }
+
+ public static final class MagnificationConfig.Builder {
+ ctor public MagnificationConfig.Builder();
+ method @NonNull public android.accessibilityservice.MagnificationConfig build();
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterX(float);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterY(float);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setMode(int);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setScale(float);
+ }
+
public final class TouchInteractionController {
method public int getDisplayId();
method public int getMaxPointerCount();
@@ -27638,6 +27660,7 @@ package android.nfc.cardemulation {
field public static final String CATEGORY_PAYMENT = "payment";
field public static final String EXTRA_CATEGORY = "category";
field public static final String EXTRA_SERVICE_COMPONENT = "component";
+ field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
@@ -40677,7 +40700,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
@@ -42745,7 +42768,7 @@ package android.telephony {
method @Nullable public String getMccString();
method @Deprecated public int getMnc();
method @Nullable public String getMncString();
- method public String getNumber();
+ method @Deprecated public String getNumber();
method public int getPortIndex();
method public int getSimSlotIndex();
method public int getSubscriptionId();
@@ -43000,7 +43023,7 @@ package android.telephony {
method public String getIccAuthentication(int, int, String);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public String getManualNetworkSelectionPlmn();
method @Nullable public String getManufacturerCode();
method @Nullable public String getManufacturerCode(int);
@@ -43085,7 +43108,7 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
- method public boolean setLine1NumberForDisplay(String, String);
+ method @Deprecated public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, boolean, int);
@@ -51422,14 +51445,18 @@ package android.view.accessibility {
public static final class AccessibilityNodeInfo.CollectionItemInfo {
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean);
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean, boolean);
+ ctor public AccessibilityNodeInfo.CollectionItemInfo(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
method public int getColumnIndex();
method public int getColumnSpan();
+ method @Nullable public String getColumnTitle();
method public int getRowIndex();
method public int getRowSpan();
+ method @Nullable public String getRowTitle();
method @Deprecated public boolean isHeading();
method public boolean isSelected();
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
+ method @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
}
public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 29a445322085..fb325a2c81d5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -961,20 +961,20 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getDeviceOwnerNameOnAnyUser();
method @Nullable public CharSequence getDeviceOwnerOrganizationName();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserProvisioningState();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
@@ -6365,14 +6365,14 @@ package android.media.tv.tuner.filter {
}
public class Filter implements java.lang.AutoCloseable {
+ method @Nullable public String acquireSharedFilterToken();
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
- method @Nullable public String createSharedFilter();
method public int flush();
+ method public void freeSharedFilterToken(@NonNull String);
method @Deprecated public int getId();
method public long getIdLong();
method public int read(@NonNull byte[], long, long);
- method public void releaseSharedFilter(@NonNull String);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int setMonitorEventMask(int);
method public int start();
@@ -8681,7 +8681,11 @@ package android.os {
}
public final class NewUserRequest {
+ method @Nullable public String getAccountName();
+ method @Nullable public android.os.PersistableBundle getAccountOptions();
+ method @Nullable public String getAccountType();
method @Nullable public String getName();
+ method @Nullable public android.graphics.Bitmap getUserIcon();
method @NonNull public String getUserType();
method public boolean isAdmin();
method public boolean isEphemeral();
@@ -8690,9 +8694,13 @@ package android.os {
public static final class NewUserRequest.Builder {
ctor public NewUserRequest.Builder();
method @NonNull public android.os.NewUserRequest build();
+ method @NonNull public android.os.NewUserRequest.Builder setAccountName(@Nullable String);
+ method @NonNull public android.os.NewUserRequest.Builder setAccountOptions(@Nullable android.os.PersistableBundle);
+ method @NonNull public android.os.NewUserRequest.Builder setAccountType(@Nullable String);
method @NonNull public android.os.NewUserRequest.Builder setAdmin();
method @NonNull public android.os.NewUserRequest.Builder setEphemeral();
method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String);
+ method @NonNull public android.os.NewUserRequest.Builder setUserIcon(@Nullable android.graphics.Bitmap);
method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String);
}
@@ -8970,6 +8978,7 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -8981,6 +8990,7 @@ package android.os {
field public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 4; // 0x4
field public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1; // 0x1
field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2
+ field public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7; // 0x7
field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
@@ -13164,6 +13174,7 @@ package android.telephony.ims {
method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags();
method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags();
method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags();
+ method @NonNull public java.util.Set<java.lang.String> getRegisteringFeatureTags();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR;
field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1
@@ -13181,6 +13192,7 @@ package android.telephony.ims {
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int);
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String);
method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteringFeatureTags(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.telephony.ims.DelegateRegistrationState build();
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f83b3a42d843..ba0b6aaf1bf2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3250,12 +3250,6 @@ package android.window {
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
}
- public final class TaskFragmentAppearedInfo implements android.os.Parcelable {
- method @NonNull public android.view.SurfaceControl getLeash();
- method @NonNull public android.window.TaskFragmentInfo getTaskFragmentInfo();
- field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentAppearedInfo> CREATOR;
- }
-
public final class TaskFragmentCreationParams implements android.os.Parcelable {
method @NonNull public android.os.IBinder getFragmentToken();
method @NonNull public android.graphics.Rect getInitialBounds();
@@ -3292,7 +3286,7 @@ package android.window {
ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
method @NonNull public java.util.concurrent.Executor getExecutor();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
- method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentAppearedInfo);
+ method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/accessibilityservice/MagnificationConfig.aidl
index 3729c09168a6..fe415a864b80 100644
--- a/core/java/android/window/TaskFragmentAppearedInfo.aidl
+++ b/core/java/android/accessibilityservice/MagnificationConfig.aidl
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package android.window;
+package android.accessibilityservice;
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-parcelable TaskFragmentAppearedInfo;
+parcelable MagnificationConfig;
diff --git a/core/java/android/accessibilityservice/MagnificationConfig.java b/core/java/android/accessibilityservice/MagnificationConfig.java
new file mode 100644
index 000000000000..8884508bb2b0
--- /dev/null
+++ b/core/java/android/accessibilityservice/MagnificationConfig.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+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;
+
+/**
+ * This class describes the magnification config for {@link AccessibilityService} to control the
+ * magnification.
+ *
+ * <p>
+ * When the magnification config uses {@link #DEFAULT_MODE},
+ * {@link AccessibilityService} will be able to control the activated magnifier on the display.
+ * If there is no magnifier activated, it controls the last-activated magnification mode.
+ * If there is no magnifier activated before, it controls full-screen magnifier by default.
+ * </p>
+ *
+ * <p>
+ * When the magnification config uses {@link #FULLSCREEN_MODE}. {@link AccessibilityService} will
+ * be able to control full-screen magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * When the magnification config uses {@link #WINDOW_MODE}. {@link AccessibilityService} will be
+ * able to control the activated window magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * If the other magnification configs, scale centerX and centerY, are not set by the
+ * {@link Builder}, the configs should be current values or default values. And the center
+ * position ordinarily is the center of the screen.
+ * </p>
+ */
+public final class MagnificationConfig implements Parcelable {
+
+ /** The controlling magnification mode. It controls the activated magnifier. */
+ public static final int DEFAULT_MODE = 0;
+ /** The controlling magnification mode. It controls fullscreen magnifier. */
+ public static final int FULLSCREEN_MODE = 1;
+ /** The controlling magnification mode. It controls window magnifier. */
+ public static final int WINDOW_MODE = 2;
+
+ @IntDef(prefix = {"MAGNIFICATION_MODE"}, value = {
+ DEFAULT_MODE,
+ FULLSCREEN_MODE,
+ WINDOW_MODE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MAGNIFICATION_MODE {
+ }
+
+ private int mMode = DEFAULT_MODE;
+ private float mScale = Float.NaN;
+ private float mCenterX = Float.NaN;
+ private float mCenterY = Float.NaN;
+
+ private MagnificationConfig() {
+ /* do nothing */
+ }
+
+ private MagnificationConfig(@NonNull Parcel parcel) {
+ mMode = parcel.readInt();
+ mScale = parcel.readFloat();
+ mCenterX = parcel.readFloat();
+ mCenterY = parcel.readFloat();
+ }
+
+ /**
+ * Returns the magnification mode that is the current activated mode or the controlling mode of
+ * the config.
+ *
+ * @return The magnification mode
+ */
+ public int getMode() {
+ return mMode;
+ }
+
+ /**
+ * Returns the magnification scale of the controlling magnifier
+ *
+ * @return the scale If the controlling magnifier is not activated, it returns 1 by default
+ */
+ public float getScale() {
+ return mScale;
+ }
+
+ /**
+ * Returns the screen-relative X coordinate of the center of the magnification viewport.
+ *
+ * @return the X coordinate. If the controlling magnifier is {@link #WINDOW_MODE} but not
+ * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #FULLSCREEN_MODE} but not enabled, it returns 0
+ */
+ public float getCenterX() {
+ return mCenterX;
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnification viewport.
+ *
+ * @return the Y coordinate If the controlling magnifier is {@link #WINDOW_MODE} but not
+ * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #FULLSCREEN_MODE} but not enabled, it returns 0
+ */
+ public float getCenterY() {
+ return mCenterY;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder("MagnificationConfig[");
+ stringBuilder.append("mode: ").append(getMode());
+ stringBuilder.append(", ");
+ stringBuilder.append("scale: ").append(getScale());
+ stringBuilder.append(", ");
+ stringBuilder.append("centerX: ").append(getCenterX());
+ stringBuilder.append(", ");
+ stringBuilder.append("centerY: ").append(getCenterY());
+ stringBuilder.append("] ");
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mMode);
+ parcel.writeFloat(mScale);
+ parcel.writeFloat(mCenterX);
+ parcel.writeFloat(mCenterY);
+ }
+
+ /**
+ * Builder for creating {@link MagnificationConfig} objects.
+ */
+ public static final class Builder {
+
+ private int mMode = DEFAULT_MODE;
+ private float mScale = Float.NaN;
+ private float mCenterX = Float.NaN;
+ private float mCenterY = Float.NaN;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the magnification mode.
+ *
+ * @param mode The magnification mode
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setMode(@MAGNIFICATION_MODE int mode) {
+ mMode = mode;
+ return this;
+ }
+
+ /**
+ * Sets the magnification scale.
+ *
+ * @param scale The magnification scale
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setScale(float scale) {
+ mScale = scale;
+ return this;
+ }
+
+ /**
+ * Sets the X coordinate of the center of the magnification viewport.
+ *
+ * @param centerX the screen-relative X coordinate around which to
+ * center and scale, or {@link Float#NaN} to leave unchanged
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setCenterX(float centerX) {
+ mCenterX = centerX;
+ return this;
+ }
+
+ /**
+ * Sets the Y coordinate of the center of the magnification viewport.
+ *
+ * @param centerY the screen-relative Y coordinate around which to
+ * center and scale, or {@link Float#NaN} to leave unchanged
+ * @return This builder
+ */
+ @NonNull
+ public MagnificationConfig.Builder setCenterY(float centerY) {
+ mCenterY = centerY;
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link MagnificationConfig}
+ */
+ @NonNull
+ public MagnificationConfig build() {
+ MagnificationConfig magnificationConfig = new MagnificationConfig();
+ magnificationConfig.mMode = mMode;
+ magnificationConfig.mScale = mScale;
+ magnificationConfig.mCenterX = mCenterX;
+ magnificationConfig.mCenterY = mCenterY;
+ return magnificationConfig;
+ }
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final @NonNull Parcelable.Creator<MagnificationConfig> CREATOR =
+ new Parcelable.Creator<MagnificationConfig>() {
+ public MagnificationConfig createFromParcel(Parcel parcel) {
+ return new MagnificationConfig(parcel);
+ }
+
+ public MagnificationConfig[] newArray(int size) {
+ return new MagnificationConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f9cc3234a9ee..9f8d24662c8d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4082,6 +4082,34 @@ public class ActivityManager {
}
/**
+ * Gets the message that is shown when a user is switched from.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingFromUserMessage() {
+ try {
+ return getService().getSwitchingFromUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the message that is shown when a user is switched to.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public @Nullable String getSwitchingToUserMessage() {
+ try {
+ return getService().getSwitchingToUserMessage();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Uses the value defined by the platform.
*
* @hide
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8bb40590d069..183e7141bfad 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -349,6 +349,8 @@ interface IActivityManager {
void setPackageScreenCompatMode(in String packageName, int mode);
@UnsupportedAppUsage
boolean switchUser(int userid);
+ String getSwitchingFromUserMessage();
+ String getSwitchingToUserMessage();
@UnsupportedAppUsage
void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 311a60d63b8e..57b319634759 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7867,7 +7867,7 @@ public class DevicePolicyManager {
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
})
public ComponentName getDeviceOwnerComponentOnAnyUser() {
return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false);
@@ -7980,8 +7980,8 @@ public class DevicePolicyManager {
* Called by the system to find out whether the device is managed by a Device Owner.
*
* @return whether the device is managed by a Device Owner.
- * @throws SecurityException if the caller is not the device owner, does not hold the
- * MANAGE_USERS permission and is not the system.
+ * @throws SecurityException if the caller is not the device owner, does not hold
+ * MANAGE_USERS or MANAGE_PROFILE_AND_DEVICE_OWNERS permissions and is not the system.
*
* @hide
*/
@@ -8002,7 +8002,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public String getDeviceOwnerNameOnAnyUser() {
throwIfParentInstance("getDeviceOwnerNameOnAnyUser");
if (mService != null) {
@@ -8392,7 +8395,10 @@ public class DevicePolicyManager {
* @throws IllegalArgumentException if the userId is invalid.
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public @Nullable String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException {
throwIfParentInstance("getProfileOwnerNameAsUser");
if (mService != null) {
@@ -11930,7 +11936,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
@UserProvisioningState
public int getUserProvisioningState() {
throwIfParentInstance("getUserProvisioningState");
@@ -13439,7 +13448,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public boolean isManagedKiosk() {
throwIfParentInstance("isManagedKiosk");
if (mService != null) {
@@ -13468,7 +13480,10 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
+ })
public boolean isUnattendedManagedKiosk() {
throwIfParentInstance("isUnattendedManagedKiosk");
if (mService != null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a5a75a4684b5..25d1d53752a7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6544,30 +6544,24 @@ public abstract class Context {
@NonNull Configuration overrideConfiguration);
/**
- * Return a new Context object for the current Context but whose resources
- * are adjusted to match the metrics of the given Display. Each call to this method
- * returns a new instance of a Context object; Context objects are not
- * shared, however common state (ClassLoader, other Resources for the
- * same configuration) may be so the Context itself can be fairly lightweight.
- *
- * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that
- * is configured to show windows on the given display call
- * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an
- * {@link android.app.Activity}.
- *
+ * Returns a new <code>Context</code> object from the current context but with resources
+ * adjusted to match the metrics of <code>display</code>. Each call to this method
+ * returns a new instance of a context object. Context objects are not shared; however,
+ * common state (such as the {@link ClassLoader} and other resources for the same
+ * configuration) can be shared, so the <code>Context</code> itself is lightweight.
* <p>
- * Note that invoking #createDisplayContext(Display) from an UI context is not regarded
- * as an UI context. In other words, it is not suggested to access UI components (such as
- * obtain a {@link WindowManager} by {@link #getSystemService(String)})
- * from the context created from #createDisplayContext(Display).
- * </p>
- *
- * @param display A {@link Display} object specifying the display for whose metrics the
- * Context's resources should be tailored.
+ * To obtain an instance of {@link WindowManager} configured to show windows on the given
+ * display, call {@link #createWindowContext(int, Bundle)} on the returned display context,
+ * then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the
+ * returned window context.
+ * <p>
+ * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI
+ * context. Do not access UI components or obtain a {@link WindowManager} from the context
+ * created by <code>createDisplayContext(Display)</code>.
*
- * @return A {@link Context} for the display.
+ * @param display The display to which the current context's resources are adjusted.
*
- * @see #getSystemService(String)
+ * @return A context for the display.
*/
@DisplayContext
public abstract Context createDisplayContext(@NonNull Display display);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3c9b1f81da8a..2b8681ae56ea 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5522,8 +5522,8 @@ public class Intent implements Parcelable, Cloneable {
/**
* A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD},
- * that specifies whether the system displayed attribution information in the
- * permission usage system UI for the chosen entry.
+ * that specifies whether the permission usage system UI is showing attribution information
+ * for the chosen entry.
*
* <p> The extra can only be true if application has specified attributionsAreUserVisible
* in its manifest. </p>
@@ -6415,6 +6415,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_RECEIVER_FROM_SHELL,
FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
FLAG_RECEIVER_OFFLOAD,
+ FLAG_RECEIVER_OFFLOAD_FOREGROUND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {}
@@ -6460,6 +6461,7 @@ public class Intent implements Parcelable, Cloneable {
FLAG_RECEIVER_FROM_SHELL,
FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS,
FLAG_RECEIVER_OFFLOAD,
+ FLAG_RECEIVER_OFFLOAD_FOREGROUND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface MutableFlags {}
@@ -6914,6 +6916,14 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int FLAG_RECEIVER_OFFLOAD = 0x80000000;
/**
+ /**
+ * If set, when sending a broadcast the recipient will run on the system dedicated queue.
+ *
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_OFFLOAD_FOREGROUND = 0x00000800;
+
+ /**
* If this is an ordered broadcast, don't allow receivers to abort the broadcast.
* They can still propagate results through to later receivers, but they can not prevent
* later receivers from seeing the broadcast.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 96a18dcac92a..3c8b6e9101b3 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1323,7 +1323,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>Maximum flashlight brightness level.</p>
* <p>If this value is greater than 1, then the device supports controlling the
* flashlight brightness level via
- * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.
+ * {android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel}.
* If this value is equal to 1, flashlight brightness control is not supported.
* The value for this key will be null for devices with no flash unit.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
@@ -1335,7 +1335,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>Default flashlight brightness level to be set via
- * {android.hardware.camera2.CameraManager#setTorchStrengthLevel}.</p>
+ * {android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel}.</p>
* <p>If flash unit is available this will be greater than or equal to 1 and less
* or equal to <code>{@link CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL android.flash.info.strengthMaximumLevel}</code>.</p>
* <p>Setting flashlight brightness above the default level
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index fd34fa4c9c7f..83e1061d8143 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -30,6 +30,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.window.DisplayWindowPolicyController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -379,6 +380,14 @@ public abstract class DisplayManagerInternal {
public abstract void onEarlyInteractivityChange(boolean interactive);
/**
+ * Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId}
+ *
+ * @param displayId The id of the display.
+ * @return The associated {@link DisplayWindowPolicyController}.
+ */
+ public abstract DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl
index e9e8935a19b2..89e9cdbd4445 100644
--- a/core/java/android/net/nsd/INsdManager.aidl
+++ b/core/java/android/net/nsd/INsdManager.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2012, The Android Open Source Project
+ * Copyright (c) 2021, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,15 @@
package android.net.nsd;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.os.Messenger;
/**
- * Interface that NsdService implements
+ * Interface that NsdService implements to connect NsdManager clients.
*
* {@hide}
*/
-interface INsdManager
-{
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- Messenger getMessenger();
- void setEnabled(boolean enable);
+interface INsdManager {
+ INsdServiceConnector connect(INsdManagerCallback cb);
}
diff --git a/core/java/android/net/nsd/INsdManagerCallback.aidl b/core/java/android/net/nsd/INsdManagerCallback.aidl
new file mode 100644
index 000000000000..1a262ec0e9dd
--- /dev/null
+++ b/core/java/android/net/nsd/INsdManagerCallback.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.nsd;
+
+import android.os.Messenger;
+import android.net.nsd.NsdServiceInfo;
+
+/**
+ * Callbacks from NsdService to NsdManager
+ * @hide
+ */
+oneway interface INsdManagerCallback {
+ void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info);
+ void onDiscoverServicesFailed(int listenerKey, int error);
+ void onServiceFound(int listenerKey, in NsdServiceInfo info);
+ void onServiceLost(int listenerKey, in NsdServiceInfo info);
+ void onStopDiscoveryFailed(int listenerKey, int error);
+ void onStopDiscoverySucceeded(int listenerKey);
+ void onRegisterServiceFailed(int listenerKey, int error);
+ void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info);
+ void onUnregisterServiceFailed(int listenerKey, int error);
+ void onUnregisterServiceSucceeded(int listenerKey);
+ void onResolveServiceFailed(int listenerKey, int error);
+ void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
+}
diff --git a/core/java/android/net/nsd/INsdServiceConnector.aidl b/core/java/android/net/nsd/INsdServiceConnector.aidl
new file mode 100644
index 000000000000..b06ae55b150e
--- /dev/null
+++ b/core/java/android/net/nsd/INsdServiceConnector.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.nsd;
+
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.NsdServiceInfo;
+import android.os.Messenger;
+
+/**
+ * Interface that NsdService implements for each NsdManager client.
+ *
+ * {@hide}
+ */
+interface INsdServiceConnector {
+ void registerService(int listenerKey, in NsdServiceInfo serviceInfo);
+ void unregisterService(int listenerKey);
+ void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo);
+ void stopDiscovery(int listenerKey);
+ void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
+ void startDaemon();
+} \ No newline at end of file
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index ae8d0101947d..6c597e26e042 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,17 +31,13 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
-import java.util.concurrent.CountDownLatch;
-
/**
* The Network Service Discovery Manager class provides the API to discover services
* on a network. As an example, if device A and device B are connected over a Wi-Fi
@@ -234,6 +230,11 @@ public final class NsdManager {
/** @hide */
public static final int NATIVE_DAEMON_EVENT = BASE + 26;
+ /** @hide */
+ public static final int REGISTER_CLIENT = BASE + 27;
+ /** @hide */
+ public static final int UNREGISTER_CLIENT = BASE + 28;
+
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -274,7 +275,7 @@ public final class NsdManager {
private static final int FIRST_LISTENER_KEY = 1;
- private final INsdManager mService;
+ private final INsdServiceConnector mService;
private final Context mContext;
private int mListenerKey = FIRST_LISTENER_KEY;
@@ -282,9 +283,7 @@ public final class NsdManager {
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
private final Object mMapLock = new Object();
- private final AsyncChannel mAsyncChannel = new AsyncChannel();
- private ServiceHandler mHandler;
- private final CountDownLatch mConnected = new CountDownLatch(1);
+ private final ServiceHandler mHandler;
/**
* Create a new Nsd instance. Applications use
@@ -295,18 +294,108 @@ public final class NsdManager {
* is a system private class.
*/
public NsdManager(Context context, INsdManager service) {
- mService = service;
mContext = context;
- init();
+
+ HandlerThread t = new HandlerThread("NsdManager");
+ t.start();
+ mHandler = new ServiceHandler(t.getLooper());
+
+ try {
+ mService = service.connect(new NsdCallbackImpl(mHandler));
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to connect to NsdService");
+ }
+
+ // Only proactively start the daemon if the target SDK < S, otherwise the internal service
+ // would automatically start/stop the native daemon as needed.
+ if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
+ try {
+ mService.startDaemon();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to proactively start daemon");
+ // Continue: the daemon can still be started on-demand later
+ }
+ }
}
- /**
- * @hide
- */
- @VisibleForTesting
- public void disconnect() {
- mAsyncChannel.disconnect();
- mHandler.getLooper().quitSafely();
+ private static class NsdCallbackImpl extends INsdManagerCallback.Stub {
+ private final Handler mServHandler;
+
+ NsdCallbackImpl(Handler serviceHandler) {
+ mServHandler = serviceHandler;
+ }
+
+ private void sendInfo(int message, int listenerKey, NsdServiceInfo info) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info));
+ }
+
+ private void sendError(int message, int listenerKey, int error) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey));
+ }
+
+ private void sendNoArg(int message, int listenerKey) {
+ mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey));
+ }
+
+ @Override
+ public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
+ sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info);
+ }
+
+ @Override
+ public void onDiscoverServicesFailed(int listenerKey, int error) {
+ sendError(DISCOVER_SERVICES_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onServiceFound(int listenerKey, NsdServiceInfo info) {
+ sendInfo(SERVICE_FOUND, listenerKey, info);
+ }
+
+ @Override
+ public void onServiceLost(int listenerKey, NsdServiceInfo info) {
+ sendInfo(SERVICE_LOST, listenerKey, info);
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(int listenerKey, int error) {
+ sendError(STOP_DISCOVERY_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onStopDiscoverySucceeded(int listenerKey) {
+ sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey);
+ }
+
+ @Override
+ public void onRegisterServiceFailed(int listenerKey, int error) {
+ sendError(REGISTER_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info);
+ }
+
+ @Override
+ public void onUnregisterServiceFailed(int listenerKey, int error) {
+ sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onUnregisterServiceSucceeded(int listenerKey) {
+ sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey);
+ }
+
+ @Override
+ public void onResolveServiceFailed(int listenerKey, int error) {
+ sendError(RESOLVE_SERVICE_FAILED, listenerKey, error);
+ }
+
+ @Override
+ public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info);
+ }
}
/**
@@ -376,19 +465,6 @@ public final class NsdManager {
public void handleMessage(Message message) {
final int what = message.what;
final int key = message.arg2;
- switch (what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- return;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- mConnected.countDown();
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel lost");
- return;
- default:
- break;
- }
final Object listener;
final NsdServiceInfo ns;
synchronized (mMapLock) {
@@ -504,36 +580,6 @@ public final class NsdManager {
}
/**
- * Initialize AsyncChannel
- */
- private void init() {
- final Messenger messenger = getMessenger();
- if (messenger == null) {
- fatal("Failed to obtain service Messenger");
- }
- HandlerThread t = new HandlerThread("NsdManager");
- t.start();
- mHandler = new ServiceHandler(t.getLooper());
- mAsyncChannel.connect(mContext, mHandler, messenger);
- try {
- mConnected.await();
- } catch (InterruptedException e) {
- fatal("Interrupted wait at init");
- }
- if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
- return;
- }
- // Only proactively start the daemon if the target SDK < S, otherwise the internal service
- // would automatically start/stop the native daemon as needed.
- mAsyncChannel.sendMessage(DAEMON_STARTUP);
- }
-
- private static void fatal(String msg) {
- Log.e(TAG, msg);
- throw new RuntimeException(msg);
- }
-
- /**
* Register a service to be discovered by other services.
*
* <p> The function call immediately returns after sending a request to register service
@@ -556,7 +602,11 @@ public final class NsdManager {
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
- mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
+ try {
+ mService.registerService(key, serviceInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -574,7 +624,11 @@ public final class NsdManager {
*/
public void unregisterService(RegistrationListener listener) {
int id = getListenerKey(listener);
- mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
+ try {
+ mService.unregisterService(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -613,7 +667,11 @@ public final class NsdManager {
s.setServiceType(serviceType);
int key = putListener(listener, s);
- mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s);
+ try {
+ mService.discoverServices(key, s);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -634,7 +692,11 @@ public final class NsdManager {
*/
public void stopServiceDiscovery(DiscoveryListener listener) {
int id = getListenerKey(listener);
- mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
+ try {
+ mService.stopDiscovery(id);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
@@ -649,29 +711,10 @@ public final class NsdManager {
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
checkServiceInfo(serviceInfo);
int key = putListener(listener, serviceInfo);
- mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
- }
-
- /** Internal use only @hide */
- public void setEnabled(boolean enabled) {
- try {
- mService.setEnabled(enabled);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get a reference to NsdService handler. This is used to establish
- * an AsyncChannel communication with the service
- *
- * @return Messenger pointing to the NsdService handler
- */
- private Messenger getMessenger() {
try {
- return mService.getMessenger();
+ mService.resolveService(key, serviceInfo);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/net/nsd/NsdServiceInfo.aidl b/core/java/android/net/nsd/NsdServiceInfo.aidl
new file mode 100644
index 000000000000..657bdd1e8706
--- /dev/null
+++ b/core/java/android/net/nsd/NsdServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.nsd;
+
+@JavaOnlyStableParcelable parcelable NsdServiceInfo; \ No newline at end of file
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 0af322e885b1..09540132fe0d 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -528,6 +528,7 @@ public final class ApduServiceInfo implements Parcelable {
public String toString() {
StringBuilder out = new StringBuilder("ApduService: ");
out.append(getComponent());
+ out.append(", UID: " + mUid);
out.append(", description: " + mDescription);
out.append(", Static AID Groups: ");
for (AidGroup aidGroup : mStaticAidGroups.values()) {
@@ -546,7 +547,8 @@ public final class ApduServiceInfo implements Parcelable {
if (!(o instanceof ApduServiceInfo)) return false;
ApduServiceInfo thatService = (ApduServiceInfo) o;
- return thatService.getComponent().equals(this.getComponent());
+ return thatService.getComponent().equals(this.getComponent())
+ && thatService.getUid() == this.getUid();
}
@Override
@@ -619,8 +621,9 @@ public final class ApduServiceInfo implements Parcelable {
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" " + getComponent() +
- " (Description: " + getDescription() + ")");
+ pw.println(" " + getComponent()
+ + " (Description: " + getDescription() + ")"
+ + " (UID: " + getUid() + ")");
if (mOnHost) {
pw.println(" On Host Service");
} else {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d498535ce52c..0a9fe90f2524 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -30,6 +30,7 @@ import android.content.pm.PackageManager;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -83,6 +84,13 @@ public final class CardEmulation {
public static final String EXTRA_SERVICE_COMPONENT = "component";
/**
+ * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}.
+ *
+ * @see #ACTION_CHANGE_DEFAULT
+ */
+ public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID";
+
+ /**
* Category used for NFC payment services.
*/
public static final String CATEGORY_PAYMENT = "payment";
@@ -269,8 +277,8 @@ public final class CardEmulation {
if (CATEGORY_PAYMENT.equals(category)) {
boolean preferForeground = false;
try {
- preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
+ preferForeground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_FOREGROUND, UserHandle.myUserId()) != 0;
} catch (SettingNotFoundException e) {
}
return preferForeground;
@@ -829,6 +837,28 @@ public final class CardEmulation {
/**
* @hide
*/
+ public boolean setDefaultForNextTap(int userId, ComponentName service) {
+ try {
+ return sService.setDefaultForNextTap(userId, service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setDefaultForNextTap(userId, service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
public List<ApduServiceInfo> getServices(String category) {
try {
return sService.getServices(mContext.getUserId(), category);
@@ -849,6 +879,28 @@ public final class CardEmulation {
}
/**
+ * @hide
+ */
+ public List<ApduServiceInfo> getServices(String category, int userId) {
+ try {
+ return sService.getServices(userId, category);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return null;
+ }
+ try {
+ return sService.getServices(userId, category);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return null;
+ }
+ }
+ }
+
+ /**
* A valid AID according to ISO/IEC 7816-4:
* <ul>
* <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index 80e8579dd73f..557e41a2b103 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -25,7 +25,6 @@ import android.content.pm.PackageManager;
import android.nfc.INfcFCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.HashMap;
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index c2b33dd51b0c..f8f7dfe034b5 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -237,6 +237,7 @@ public final class NfcFServiceInfo implements Parcelable {
public String toString() {
StringBuilder out = new StringBuilder("NfcFService: ");
out.append(getComponent());
+ out.append(", UID: " + mUid);
out.append(", description: " + mDescription);
out.append(", System Code: " + mSystemCode);
if (mDynamicSystemCode != null) {
@@ -257,6 +258,7 @@ public final class NfcFServiceInfo implements Parcelable {
NfcFServiceInfo thatService = (NfcFServiceInfo) o;
if (!thatService.getComponent().equals(this.getComponent())) return false;
+ if (thatService.getUid() != this.getUid()) return false;
if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
@@ -321,8 +323,9 @@ public final class NfcFServiceInfo implements Parcelable {
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" " + getComponent() +
- " (Description: " + getDescription() + ")");
+ pw.println(" " + getComponent()
+ + " (Description: " + getDescription() + ")"
+ + " (UID: " + getUid() + ")");
pw.println(" System Code: " + getSystemCode());
pw.println(" NFCID2: " + getNfcid2());
pw.println(" T3tPmm: " + getT3tPmm());
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index b83970625ee4..50ca9ff3576f 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -20,6 +20,7 @@ package android.os;
import android.os.Bundle;
import android.os.IUserRestrictionsListener;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.content.pm.UserInfo;
import android.content.IntentSender;
@@ -91,6 +92,9 @@ interface IUserManager {
boolean markGuestForDeletion(int userId);
UserInfo findCurrentGuestUser();
boolean isQuietModeEnabled(int userId);
+ UserHandle createUserWithAttributes(in String userName, in String userType, int flags,
+ in Bitmap userIcon,
+ in String accountName, in String accountType, in PersistableBundle accountOptions);
void setSeedAccountData(int userId, in String accountName,
in String accountType, in PersistableBundle accountOptions, boolean persist);
String getSeedAccountName(int userId);
@@ -98,6 +102,7 @@ interface IUserManager {
PersistableBundle getSeedAccountOptions(int userId);
void clearSeedAccountData(int userId);
boolean someUserHasSeedAccount(in String accountName, in String accountType);
+ boolean someUserHasAccount(in String accountName, in String accountType);
boolean isProfile(int userId);
boolean isManagedProfile(int userId);
boolean isCloneProfile(int userId);
diff --git a/core/java/android/os/NewUserRequest.java b/core/java/android/os/NewUserRequest.java
index 2ebc01f2d3d0..b0e1f91c47c3 100644
--- a/core/java/android/os/NewUserRequest.java
+++ b/core/java/android/os/NewUserRequest.java
@@ -17,7 +17,11 @@ package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
/**
* Contains necessary information to create user using
@@ -26,6 +30,7 @@ import android.annotation.SystemApi;
* @hide
*/
@SystemApi
+@SuppressLint("PackageLayering")
public final class NewUserRequest {
@Nullable
private final String mName;
@@ -33,16 +38,24 @@ public final class NewUserRequest {
private final boolean mEphemeral;
@NonNull
private final String mUserType;
+ private final Bitmap mUserIcon;
+ private final String mAccountName;
+ private final String mAccountType;
+ private final PersistableBundle mAccountOptions;
private NewUserRequest(Builder builder) {
mName = builder.mName;
mAdmin = builder.mAdmin;
mEphemeral = builder.mEphemeral;
mUserType = builder.mUserType;
+ mUserIcon = builder.mUserIcon;
+ mAccountName = builder.mAccountName;
+ mAccountType = builder.mAccountType;
+ mAccountOptions = builder.mAccountOptions;
}
/**
- * Gets the user name.
+ * Returns the name of the user.
*/
@Nullable
public String getName() {
@@ -50,7 +63,7 @@ public final class NewUserRequest {
}
/**
- * Is user Ephemenral?
+ * Returns whether the user is ephemeral.
*
* <p> Ephemeral user will be removed after leaving the foreground.
*/
@@ -59,7 +72,7 @@ public final class NewUserRequest {
}
/**
- * Is user Admin?
+ * Returns whether the user is an admin.
*
* <p> Admin user is with administrative privileges and such user can create and
* delete users.
@@ -69,7 +82,17 @@ public final class NewUserRequest {
}
/**
- * Gets user type.
+ * Returns the calculated flags for user creation.
+ */
+ int getFlags() {
+ int flags = 0;
+ if (isAdmin()) flags |= UserInfo.FLAG_ADMIN;
+ if (isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
+ return flags;
+ }
+
+ /**
+ * Returns the user type.
*
* <p> Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
* {@link USER_TYPE_FULL_GUEST}
@@ -79,25 +102,71 @@ public final class NewUserRequest {
return mUserType;
}
+ /**
+ * Returns the user icon.
+ */
+ @Nullable
+ public Bitmap getUserIcon() {
+ return mUserIcon;
+ }
+
+ /**
+ * Returns the account name.
+ */
+ @Nullable
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ /**
+ * Returns the account type.
+ */
+ @Nullable
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ /**
+ * Returns the account options.
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ public PersistableBundle getAccountOptions() {
+ return mAccountOptions;
+ }
+
@Override
public String toString() {
- return String.format(
- "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName,
- mUserType, mAdmin, mEphemeral);
+ return "NewUserRequest{"
+ + "mName='" + mName + '\''
+ + ", mAdmin=" + mAdmin
+ + ", mEphemeral=" + mEphemeral
+ + ", mUserType='" + mUserType + '\''
+ + ", mAccountName='" + mAccountName + '\''
+ + ", mAccountType='" + mAccountType + '\''
+ + ", mAccountOptions=" + mAccountOptions
+ + '}';
}
/**
* Builder for building {@link NewUserRequest}
*/
+ @SuppressLint("PackageLayering")
public static final class Builder {
private String mName;
private boolean mAdmin;
private boolean mEphemeral;
private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY;
+ private Bitmap mUserIcon;
+ private String mAccountName;
+ private String mAccountType;
+ private PersistableBundle mAccountOptions;
/**
* Sets user name.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setName(@Nullable String name) {
@@ -110,6 +179,8 @@ public final class NewUserRequest {
*
* <p> Admin user is with administrative privileges and such user can create
* and delete users.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setAdmin() {
@@ -121,6 +192,8 @@ public final class NewUserRequest {
* Sets user as ephemeral.
*
* <p> Ephemeral user will be removed after leaving the foreground.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setEphemeral() {
@@ -134,6 +207,8 @@ public final class NewUserRequest {
* Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and
* {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is
* {@link UserManager.USER_TYPE_FULL_SECONDARY}.
+ *
+ * @return This object for method chaining.
*/
@NonNull
public Builder setUserType(@NonNull String type) {
@@ -142,6 +217,54 @@ public final class NewUserRequest {
}
/**
+ * Sets user icon.
+ *
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setUserIcon(@Nullable Bitmap userIcon) {
+ mUserIcon = userIcon;
+ return this;
+ }
+
+ /**
+ * Sets account name that will be used by the setup wizard to initialize the user.
+ *
+ * @see android.accounts.Account
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountName(@Nullable String accountName) {
+ mAccountName = accountName;
+ return this;
+ }
+
+ /**
+ * Sets account type for the account to be created. This is required if the account name
+ * is not null. This will be used by the setup wizard to initialize the user.
+ *
+ * @see android.accounts.Account
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountType(@Nullable String accountType) {
+ mAccountType = accountType;
+ return this;
+ }
+
+ /**
+ * Sets account options that can contain account-specific extra information
+ * to be used by setup wizard to initialize the account for the user.
+ *
+ * @return This object for method chaining.
+ */
+ @NonNull
+ public Builder setAccountOptions(@Nullable PersistableBundle accountOptions) {
+ mAccountOptions = accountOptions;
+ return this;
+ }
+
+ /**
* Builds {@link NewUserRequest}
*
* @throws IllegalStateException if builder is configured with incompatible properties and
@@ -165,6 +288,11 @@ public final class NewUserRequest {
&& mUserType != UserManager.USER_TYPE_FULL_GUEST) {
throw new IllegalStateException("Unsupported user type: " + mUserType);
}
+
+ if (TextUtils.isEmpty(mAccountName) != TextUtils.isEmpty(mAccountType)) {
+ throw new IllegalStateException(
+ "Account name and account type should be provided together.");
+ }
}
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 94375c0e949f..cf4ce9b43cf2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -70,6 +70,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -1661,6 +1662,14 @@ public class UserManager {
public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
/**
+ * Indicates user operation failed because a user with that account already exists.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7;
+
+ /**
* Result returned from various user operations.
*
* @hide
@@ -1673,7 +1682,8 @@ public class UserManager {
USER_OPERATION_ERROR_MAX_RUNNING_USERS,
USER_OPERATION_ERROR_CURRENT_USER,
USER_OPERATION_ERROR_LOW_STORAGE,
- USER_OPERATION_ERROR_MAX_USERS
+ USER_OPERATION_ERROR_MAX_USERS,
+ USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS
})
public @interface UserOperationResult {}
@@ -3159,26 +3169,24 @@ public class UserManager {
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) {
- UserInfo user = null;
- int operationResult = USER_OPERATION_ERROR_UNKNOWN;
try {
- user = createUser(newUserRequest.getName(), newUserRequest.getUserType(),
- determineFlagsForUserCreation(newUserRequest));
- } catch (UserOperationException e) {
+ final UserHandle userHandle = mService.createUserWithAttributes(
+ newUserRequest.getName(),
+ newUserRequest.getUserType(),
+ newUserRequest.getFlags(),
+ newUserRequest.getUserIcon(),
+ newUserRequest.getAccountName(),
+ newUserRequest.getAccountType(),
+ newUserRequest.getAccountOptions());
+
+ return new NewUserResponse(userHandle, USER_OPERATION_SUCCESS);
+
+ } catch (ServiceSpecificException e) {
Log.w(TAG, "Exception while creating user " + newUserRequest, e);
- operationResult = e.getUserOperationResult();
- }
- if (user == null) {
- return new NewUserResponse(null, operationResult);
+ return new NewUserResponse(null, e.errorCode);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
- return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS);
- }
-
- private int determineFlagsForUserCreation(NewUserRequest newUserRequest) {
- int flags = 0;
- if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN;
- if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL;
- return flags;
}
/**
@@ -4913,12 +4921,12 @@ public class UserManager {
}
/**
- * @hide
* Checks if any uninitialized user has the specific seed account name and type.
*
* @param accountName The account name to check for
* @param accountType The account type of the account to check for
* @return whether the seed account was found
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean someUserHasSeedAccount(String accountName, String accountType) {
@@ -4930,6 +4938,29 @@ public class UserManager {
}
/**
+ * Checks if any initialized or uninitialized user has the specific account name and type.
+ *
+ * @param accountName The account name to check for
+ * @param accountType The account type of the account to check for
+ * @return whether the account was found
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ public boolean someUserHasAccount(
+ @NonNull String accountName, @NonNull String accountType) {
+ Objects.requireNonNull(accountName, "accountName must not be null");
+ Objects.requireNonNull(accountType, "accountType must not be null");
+
+ try {
+ return mService.someUserHasAccount(accountName, accountType);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
* User that enforces a restriction.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ae09b45a8f99..cc95c1f6c60b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4537,6 +4537,25 @@ public final class Settings {
"haptic_feedback_intensity";
/**
+ * The intensity of haptic feedback vibrations for interaction with hardware components from
+ * the device, like buttons and sensors, if configurable.
+ *
+ * Not all devices are capable of changing their feedback intensity; on these devices
+ * there will likely be no difference between the various vibration intensities except for
+ * intensity 0 (off) and the rest.
+ *
+ * <b>Values:</b><br/>
+ * 0 - Vibration is disabled<br/>
+ * 1 - Weak vibrations<br/>
+ * 2 - Medium vibrations<br/>
+ * 3 - Strong vibrations
+ * @hide
+ */
+ @Readable
+ public static final String HARDWARE_HAPTIC_FEEDBACK_INTENSITY =
+ "hardware_haptic_feedback_intensity";
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 1b38f590edd6..5d84af051382 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3584,6 +3584,23 @@ public final class Telephony {
"content://telephony/carriers/enforce_managed");
/**
+ * The {@code content://} style URL for the perferred APN used for internet.
+ *
+ * @hide
+ */
+ public static final Uri PREFERRED_APN_URI = Uri.parse(
+ "content://telephony/carriers/preferapn/subId/");
+
+ /**
+ * The {@code content://} style URL for the perferred APN set id.
+ *
+ * @hide
+ */
+ public static final Uri PREFERRED_APN_SET_URI = Uri.parse(
+ "content://telephony/carriers/preferapnset/subId/");
+
+
+ /**
* The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
* @hide
*/
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index ec613edeedb4..c5bc99d042d7 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -153,11 +153,20 @@ public class HapticFeedbackConstants {
/**
* Invocation of the voice assistant via hardware button.
+ * This is a private constant. Feel free to renumber as desired.
* @hide
*/
public static final int ASSISTANT_BUTTON = 10002;
/**
+ * The user has performed a long press on the power button hardware that is resulting
+ * in an action being performed.
+ * This is a private constant. Feel free to renumber as desired.
+ * @hide
+ */
+ public static final int LONG_PRESS_POWER_BUTTON = 10003;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1460cb251c72..c3a638c4c36a 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -295,7 +295,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
/** Key code constant: Fast Forward media key. */
public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
/** Key code constant: Mute key.
- * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */
+ * Mute key for the microphone (unlike {@link #KEYCODE_VOLUME_MUTE}, which is the speaker mute
+ * key). */
public static final int KEYCODE_MUTE = 91;
/** Key code constant: Page Up key. */
public static final int KEYCODE_PAGE_UP = 92;
@@ -482,9 +483,10 @@ public class KeyEvent extends InputEvent implements Parcelable {
/** Key code constant: Numeric keypad ')' key. */
public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
/** Key code constant: Volume Mute key.
- * Mutes the speaker, unlike {@link #KEYCODE_MUTE}.
- * This key should normally be implemented as a toggle such that the first press
- * mutes the speaker and the second press restores the original volume. */
+ * Mute key for speaker (unlike {@link #KEYCODE_MUTE}, which is the mute key for the
+ * microphone). This key should normally be implemented as a toggle such that the first press
+ * mutes the speaker and the second press restores the original volume.
+ */
public static final int KEYCODE_VOLUME_MUTE = 164;
/** Key code constant: Info key.
* Common on TV remotes to show additional information related to what is
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3b4fcc0adccb..7fe810acd6f3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -129,17 +129,15 @@ import java.util.function.Consumer;
/**
* The interface that apps use to talk to the window manager.
- * </p><p>
- * Each window manager instance is bound to a particular {@link Display}.
- * To obtain a {@link WindowManager} for a different display, use
- * {@link Context#createDisplayContext} to obtain a {@link Context} for that
- * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code>
- * to get the WindowManager.
- * </p><p>
- * The simplest way to show a window on another display is to create a
- * {@link Presentation}. The presentation will automatically obtain a
- * {@link WindowManager} and {@link Context} for that display.
- * </p>
+ * <p>
+ * Each window manager instance is bound to a {@link Display}. To obtain the
+ * <code>WindowManager</code> associated with a display,
+ * call {@link Context#createWindowContext(Display, int, Bundle)} to get the display's UI context,
+ * then call {@link Context#getSystemService(String)} or {@link Context#getSystemService(Class)} on
+ * the UI context.
+ * <p>
+ * The simplest way to show a window on a particular display is to create a {@link Presentation},
+ * which automatically obtains a <code>WindowManager</code> and context for the display.
*/
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 4730eaa8eee9..7680aa6c61fd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3957,8 +3957,10 @@ public class AccessibilityNodeInfo implements Parcelable {
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeString(mCollectionItemInfo.getRowTitle());
parcel.writeInt(mCollectionItemInfo.getRowIndex());
parcel.writeInt(mCollectionItemInfo.getRowSpan());
+ parcel.writeString(mCollectionItemInfo.getColumnTitle());
parcel.writeInt(mCollectionItemInfo.getColumnIndex());
parcel.writeInt(mCollectionItemInfo.getColumnSpan());
parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
@@ -4100,8 +4102,9 @@ public class AccessibilityNodeInfo implements Parcelable {
ci.mHierarchical, ci.mSelectionMode);
CollectionItemInfo cii = other.mCollectionItemInfo;
mCollectionItemInfo = (cii == null) ? null
- : new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
- cii.mColumnSpan, cii.mHeading, cii.mSelected);
+ : new CollectionItemInfo(cii.mRowTitle, cii.mRowIndex, cii.mRowSpan,
+ cii.mColumnTitle, cii.mColumnIndex, cii.mColumnSpan,
+ cii.mHeading, cii.mSelected);
ExtraRenderingInfo ti = other.mExtraRenderingInfo;
mExtraRenderingInfo = (ti == null) ? null
: new ExtraRenderingInfo(ti);
@@ -4221,8 +4224,10 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
? CollectionItemInfo.obtain(
+ parcel.readString(),
parcel.readInt(),
parcel.readInt(),
+ parcel.readString(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt() == 1,
@@ -5570,8 +5575,9 @@ public class AccessibilityNodeInfo implements Parcelable {
* @hide
*/
public static CollectionItemInfo obtain(CollectionItemInfo other) {
- return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
- other.mColumnSpan, other.mHeading, other.mSelected);
+ return CollectionItemInfo.obtain(other.mRowTitle, other.mRowIndex, other.mRowSpan,
+ other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
+ other.mSelected);
}
/**
@@ -5612,10 +5618,36 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading, boolean selected) {
+ return obtain(null, rowIndex, rowSpan, null, columnIndex,
+ columnSpan, heading, selected);
+ }
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * constructor {@link
+ * AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
+ * int, int, int, boolean, boolean)} instead.
+ *
+ * @param rowTitle The row title at which the item is located.
+ * @param rowIndex The row index at which the item is located.
+ * @param rowSpan The number of rows the item spans.
+ * @param columnTitle The column title at which the item is located.
+ * @param columnIndex The column index at which the item is located.
+ * @param columnSpan The number of columns the item spans.
+ * @param heading Whether the item is a heading. (Prefer
+ * {@link AccessibilityNodeInfo#setHeading(boolean)})
+ * @param selected Whether the item is selected.
+ */
+ @NonNull
+ public static CollectionItemInfo obtain(@Nullable String rowTitle, int rowIndex,
+ int rowSpan, @Nullable String columnTitle, int columnIndex, int columnSpan,
+ boolean heading, boolean selected) {
final CollectionItemInfo info = sPool.acquire();
if (info == null) {
- return new CollectionItemInfo(
- rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
+ return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle,
+ columnIndex, columnSpan, heading, selected);
}
info.mRowIndex = rowIndex;
@@ -5624,6 +5656,8 @@ public class AccessibilityNodeInfo implements Parcelable {
info.mColumnSpan = columnSpan;
info.mHeading = heading;
info.mSelected = selected;
+ info.mRowTitle = rowTitle;
+ info.mColumnTitle = columnTitle;
return info;
}
@@ -5633,6 +5667,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private int mColumnSpan;
private int mRowSpan;
private boolean mSelected;
+ private String mRowTitle;
+ private String mColumnTitle;
/**
* Creates a new instance.
@@ -5660,12 +5696,33 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
boolean heading, boolean selected) {
+ this(null, rowIndex, rowSpan, null, columnIndex, columnSpan,
+ heading, selected);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param rowTitle The row title at which the item is located.
+ * @param rowIndex The row index at which the item is located.
+ * @param rowSpan The number of rows the item spans.
+ * @param columnTitle The column title at which the item is located.
+ * @param columnIndex The column index at which the item is located.
+ * @param columnSpan The number of columns the item spans.
+ * @param heading Whether the item is a heading.
+ * @param selected Whether the item is selected.
+ */
+ public CollectionItemInfo(@Nullable String rowTitle, int rowIndex, int rowSpan,
+ @Nullable String columnTitle, int columnIndex, int columnSpan, boolean heading,
+ boolean selected) {
mRowIndex = rowIndex;
mRowSpan = rowSpan;
mColumnIndex = columnIndex;
mColumnSpan = columnSpan;
mHeading = heading;
mSelected = selected;
+ mRowTitle = rowTitle;
+ mColumnTitle = columnTitle;
}
/**
@@ -5725,6 +5782,26 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the row title at which the item is located.
+ *
+ * @return The row title.
+ */
+ @Nullable
+ public String getRowTitle() {
+ return mRowTitle;
+ }
+
+ /**
+ * Gets the column title at which the item is located.
+ *
+ * @return The column title.
+ */
+ @Nullable
+ public String getColumnTitle() {
+ return mColumnTitle;
+ }
+
+ /**
* Recycles this instance.
*
* <p>In most situations object pooling is not beneficial, and recycling is not necessary.
@@ -5741,6 +5818,8 @@ public class AccessibilityNodeInfo implements Parcelable {
mRowSpan = 0;
mHeading = false;
mSelected = false;
+ mRowTitle = null;
+ mColumnTitle = null;
}
}
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
new file mode 100644
index 000000000000..7677b89dff96
--- /dev/null
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Abstract class to control the policies of the windows that can be displayed on the virtual
+ * display.
+ *
+ * @hide
+ */
+public abstract class DisplayWindowPolicyController {
+ /**
+ * The window flags that we are interested in.
+ * @see android.view.WindowManager.LayoutParams
+ * @see #keepActivityOnWindowFlagsChanged
+ */
+ private int mWindowFlags;
+
+ /**
+ * Returns {@code true} if the given window flags contain the flags that we're interested in.
+ */
+ public final boolean isInterestedWindowFlags(int windowFlags) {
+ return (mWindowFlags & windowFlags) != 0;
+ }
+
+ /**
+ * Sets the window flags that we’re interested in and expected
+ * #keepActivityOnWindowFlagsChanged to be called if any changes.
+ */
+ public final void setInterestedWindowFlags(int windowFlags) {
+ mWindowFlags = windowFlags;
+ }
+
+ /**
+ * Returns {@code true} if the given activities can be displayed on this virtual display.
+ */
+ public abstract boolean canContainActivities(@NonNull List<ActivityInfo> activities);
+
+ /**
+ * Called when an Activity window is layouted with the new changes where contains the
+ * window flags that we’re interested in.
+ * Returns {@code false} if the Activity cannot remain on the display and the activity task will
+ * be moved back to default display.
+ */
+ public abstract boolean keepActivityOnWindowFlagsChanged(
+ ActivityInfo activityInfo, int windowFlags);
+
+ /**
+ * This is called when the top activity of the display is changed.
+ */
+ public void onTopActivityChanged(ComponentName topActivity, int uid) {}
+
+ /**
+ * This is called when the apps that contains running activities on the display has changed.
+ */
+ public void onRunningAppsChanged(int[] runningUids) {}
+
+ /** Dump debug data */
+ public void dump(String prefix, final PrintWriter pw) {
+ pw.println(prefix + "DisplayWindowPolicyController{" + super.toString() + "}");
+ pw.println(prefix + " mWindowFlags=" + mWindowFlags);
+ }
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 5eb432e785ee..cdfa206423c2 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -19,12 +19,11 @@ package android.window;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
deleted file mode 100644
index 89d9a9508a71..000000000000
--- a/core/java/android/window/TaskFragmentAppearedInfo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.window;
-
-import android.annotation.NonNull;
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.SurfaceControl;
-
-/**
- * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
- * @hide
- */
-@TestApi
-public final class TaskFragmentAppearedInfo implements Parcelable {
-
- @NonNull
- private final TaskFragmentInfo mTaskFragmentInfo;
-
- @NonNull
- private final SurfaceControl mLeash;
-
- /** @hide */
- public TaskFragmentAppearedInfo(
- @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
- mTaskFragmentInfo = taskFragmentInfo;
- mLeash = leash;
- }
-
- @NonNull
- public TaskFragmentInfo getTaskFragmentInfo() {
- return mTaskFragmentInfo;
- }
-
- @NonNull
- public SurfaceControl getLeash() {
- return mLeash;
- }
-
- private TaskFragmentAppearedInfo(Parcel in) {
- mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
- mLeash = in.readTypedObject(SurfaceControl.CREATOR);
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedObject(mTaskFragmentInfo, flags);
- dest.writeTypedObject(mLeash, flags);
- }
-
- @NonNull
- public static final Creator<TaskFragmentAppearedInfo> CREATOR =
- new Creator<TaskFragmentAppearedInfo>() {
- @Override
- public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
- return new TaskFragmentAppearedInfo(in);
- }
-
- @Override
- public TaskFragmentAppearedInfo[] newArray(int size) {
- return new TaskFragmentAppearedInfo[size];
- }
- };
-
- @Override
- public String toString() {
- return "TaskFragmentAppearedInfo{"
- + " taskFragmentInfo=" + mTaskFragmentInfo
- + "}";
- }
-
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 337c5a14e9d3..7e7d37083b5b 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -120,8 +120,7 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
/** Called when a TaskFragment is created and organized by this organizer. */
- public void onTaskFragmentAppeared(
- @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/** Called when the status of an organized TaskFragment is changed. */
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
@@ -169,7 +168,7 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fd9ad0d9bcfd..359c382f51bc 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -186,6 +186,17 @@ public class ChooserActivity extends ResolverActivity implements
= "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
/**
+ * Boolean extra added to "unbundled Sharesheet" delegation intents to signal whether the app
+ * prediction service is available. Our query of the service <em>availability</em> depends on
+ * privileges that are only available in the system, even though the service itself would then
+ * be available to the unbundled component. For now, we just include the query result as part of
+ * the handover intent.
+ * TODO: investigate whether the privileged query is necessary to determine the availability.
+ */
+ protected static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
+ "com.android.internal.app.ChooserActivity.EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE";
+
+ /**
* Transition name for the first image preview.
* To be used for shared element transition into this activity.
* @hide
@@ -757,6 +768,11 @@ public class ChooserActivity extends ResolverActivity implements
delegationIntent.setComponent(delegateActivity);
delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
+
+ // Query prediction availability; mIsAppPredictorComponentAvailable isn't initialized.
+ delegationIntent.putExtra(
+ EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE, isAppPredictionServiceAvailable());
+
delegationIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
// Don't close until the delegate finishes, or the token will be invalidated.
@@ -971,7 +987,8 @@ public class ChooserActivity extends ResolverActivity implements
return false;
}
- // Check if the app prediction component actually exists on the device.
+ // Check if the app prediction component actually exists on the device. The component is
+ // only visible when this is running in a system activity; otherwise this check will fail.
Intent intent = new Intent();
intent.setComponent(appPredictionComponentName);
if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d3ee98a01951..601280a25873 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6395,6 +6395,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.compos.IsolatedCompilationJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.PruneInstantAppsJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2c60fbd8f207..a36785f37d3f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4943,9 +4943,10 @@
the app in the letterbox mode. -->
<item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
- <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
- min between device bottom corner radii will be used instead. -->
- <integer name="config_letterboxActivityCornersRadius">-1</integer>
+ <!-- Corners radius for activity presented the letterbox mode. Values < 0 enable rounded
+ corners with radius equal to min between device bottom corner radii. Default 0 value turns
+ off rounded corners logic in LetterboxUiController. -->
+ <integer name="config_letterboxActivityCornersRadius">0</integer>
<!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
ignored and 0 is used. -->
diff --git a/data/etc/com.android.intentresolver.xml b/data/etc/com.android.intentresolver.xml
index 0f1c4673562e..f4e94ad0e04b 100644
--- a/data/etc/com.android.intentresolver.xml
+++ b/data/etc/com.android.intentresolver.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.intentresolver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS"/>
</privapp-permissions>
</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 85ef270ac49d..df751fc9fa48 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -27,8 +27,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.SurfaceControl;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
@@ -51,9 +49,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
/** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
- /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
- private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
-
/**
* Mapping from the client assigned unique token to the TaskFragment parent
* {@link Configuration}.
@@ -67,7 +62,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
* Callback that notifies the controller about changes to task fragments.
*/
interface TaskFragmentCallback {
- void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@@ -259,15 +254,12 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
- final IBinder fragmentToken = info.getFragmentToken();
- final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
- mFragmentInfos.put(fragmentToken, info);
- mFragmentLeashes.put(fragmentToken, leash);
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+ mFragmentInfos.put(fragmentToken, taskFragmentInfo);
if (mCallback != null) {
- mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+ mCallback.onTaskFragmentAppeared(taskFragmentInfo);
}
}
@@ -284,7 +276,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
- mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 68c19041940c..fe6c7ba3b24c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -37,7 +37,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -110,14 +109,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
- TaskFragmentContainer container = getContainer(
- taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken());
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
}
- container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+ container.setInfo(taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
}
diff --git a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
new file mode 100644
index 000000000000..329e5b9b31a0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
index 94165a11eccb..22cd384e1be0 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="@color/size_compat_hint_bubble"/>
+ <solid android:color="@color/size_compat_background"/>
<corners android:radius="@dimen/size_compat_hint_corner_radius"/>
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
index a8f0f76ef27f..af9063a94afb 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml
@@ -20,6 +20,6 @@
android:viewportWidth="10"
android:viewportHeight="8">
<path
- android:fillColor="@color/size_compat_hint_bubble"
+ android:fillColor="@color/size_compat_background"
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index 3e486df71f91..18caa3582537 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -20,16 +20,16 @@
android:viewportWidth="48"
android:viewportHeight="48">
<path
- android:fillColor="#53534D"
+ android:fillColor="@color/size_compat_background"
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
<group
android:translateX="12"
android:translateY="12">
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
<path
- android:fillColor="#E4E3DA"
+ android:fillColor="@color/size_compat_text"
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
new file mode 100644
index 000000000000..95decff24ac4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/size_compat_background_ripple">
+ <item android:drawable="@drawable/size_compat_restart_button"/>
+</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 17347f627049..d0e7c42dbf8b 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -40,7 +40,7 @@
android:padding="16dp"
android:text="@string/restart_button_description"
android:textAlignment="viewStart"
- android:textColor="#E4E3DA"
+ android:textColor="@color/size_compat_text"
android:textSize="14sp"/>
<ImageView
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
index 47e76f061877..82ebee263a64 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_ui.xml
@@ -30,7 +30,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button"
+ android:src="@drawable/size_compat_restart_button_ripple"
android:background="@android:color/transparent"
android:contentDescription="@string/restart_button_description"/>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b25a2189cd4d..23a21724e43d 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -29,7 +29,10 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
- <color name="size_compat_hint_bubble">#30312B</color>
+
+ <!-- Size Compat Restart Button -->
+ <color name="size_compat_background">@android:color/system_neutral1_800</color>
+ <color name="size_compat_text">@android:color/system_neutral1_50</color>
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index d239e56bfd69..9ceed243faa5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -244,15 +244,24 @@ public abstract class WMShellBaseModule {
// Fullscreen
//
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract FullscreenTaskListener optionalFullscreenTaskListener();
+
@WMSingleton
@Provides
static FullscreenTaskListener provideFullscreenTaskListener(
+ @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
- Optional<RecentTasksController> recentTasksOptional
- ) {
- return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
- recentTasksOptional);
+ Optional<RecentTasksController> recentTasksOptional) {
+ if (fullscreenTaskListener.isPresent()) {
+ return fullscreenTaskListener.get();
+ } else {
+ return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
+ recentTasksOptional);
+ }
}
//
@@ -337,7 +346,7 @@ public abstract class WMShellBaseModule {
@Provides
static Optional<OneHandedController> providesOneHandedController(
@DynamicOverride Optional<OneHandedController> oneHandedController) {
- if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
return oneHandedController;
}
return Optional.empty();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 00083d986dbe..83390a539043 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -57,7 +57,7 @@ import java.lang.annotation.RetentionPolicy;
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
private static final String TAG = "TvPipController";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
private static final int NONEXISTENT_TASK_ID = -1;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 7cf3bafe499a..a006f308d694 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -111,6 +111,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
if (taskId1 == taskId2) {
return;
}
+ if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
+ && mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
+ // If the two tasks are already paired and the bounds are the same, then skip updating
+ return;
+ }
// Remove any previous pairs
removeSplitPair(taskId1);
removeSplitPair(taskId2);
@@ -121,6 +126,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mSplitTasks.put(taskId2, taskId1);
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
+ notifyRecentTasksChanged();
}
/**
@@ -133,6 +139,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
mSplitTasks.delete(pairedTaskId);
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
+ notifyRecentTasksChanged();
}
}
@@ -217,7 +224,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID) {
+ if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
index aadf792c572f..a0c84cc33ebd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
@@ -19,6 +19,8 @@ import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Container of various information needed to display split screen
* tasks/leashes/etc in Launcher
@@ -93,6 +95,24 @@ public class StagedSplitBounds implements Parcelable {
}
@Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StagedSplitBounds)) {
+ return false;
+ }
+ // Only need to check the base fields (the other fields are derived from these)
+ final StagedSplitBounds other = (StagedSplitBounds) obj;
+ return Objects.equals(leftTopBounds, other.leftTopBounds)
+ && Objects.equals(rightBottomBounds, other.rightBottomBounds)
+ && leftTopTaskId == other.leftTopTaskId
+ && rightBottomTaskId == other.rightBottomTaskId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
+ }
+
+ @Override
public String toString() {
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 19a5417aace6..50f6bd7b4927 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static java.lang.Integer.MAX_VALUE;
@@ -83,6 +84,36 @@ public class RecentTasksControllerTest extends ShellTestCase {
}
@Test
+ public void testAddRemoveSplitNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(StagedSplitBounds.class));
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+
+ reset(mRecentTasksController);
+ mRecentTasksController.removeSplitPair(t1.taskId);
+ verify(mRecentTasksController).notifyRecentTasksChanged();
+ }
+
+ @Test
+ public void testAddSameSplitBoundsInfoSkipNotifyChange() {
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ setRawList(t1, t2);
+
+ // Verify only one update if the split info is the same
+ StagedSplitBounds bounds1 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
+ StagedSplitBounds bounds2 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
+ verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
+ }
+
+ @Test
public void testGetRecentTasks() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5aad821ad59f..6fc251dc815c 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -118,6 +118,24 @@ void WebViewFunctor::onRemovedFromTree() {
}
}
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ if (!Properties::enableWebViewOverlays) return false;
+
+ renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
+ if (!activeContext) return false;
+
+ ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ if (!rootSurfaceControl) return false;
+
+ int32_t rgid = activeContext->getSurfaceControlGenerationId();
+ if (mParentSurfaceControlGenerationId != rgid) {
+ reparentSurfaceControl(rootSurfaceControl);
+ mParentSurfaceControlGenerationId = rgid;
+ }
+
+ return true;
+}
+
void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
ATRACE_NAME("WebViewFunctor::drawGl");
if (!mHasContext) {
@@ -131,20 +149,8 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
.mergeTransaction = currentFunctor.mergeTransaction,
};
- if (Properties::enableWebViewOverlays && !drawInfo.isLayer) {
- renderthread::CanvasContext* activeContext =
- renderthread::CanvasContext::getActiveContext();
- if (activeContext != nullptr) {
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
- if (rootSurfaceControl) {
- overlayParams.overlaysMode = OverlaysMode::Enabled;
- int32_t rgid = activeContext->getSurfaceControlGenerationId();
- if (mParentSurfaceControlGenerationId != rgid) {
- reparentSurfaceControl(rootSurfaceControl);
- mParentSurfaceControlGenerationId = rgid;
- }
- }
- }
+ if (!drawInfo.isLayer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
}
mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams);
@@ -170,7 +176,10 @@ void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
.mergeTransaction = currentFunctor.mergeTransaction,
};
- // TODO, enable surface control once offscreen mode figured out
+ if (!params.is_layer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
+ }
+
mCallbacks.vk.draw(mFunctor, mData, params, overlayParams);
}
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index f28f310993ec..0a02f2d4b720 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -88,6 +88,7 @@ public:
}
private:
+ bool prepareRootSurfaceControl();
void reparentSurfaceControl(ASurfaceControl* parent);
private:
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 8abf4534a04c..e6ef95b9cf91 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -72,6 +72,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
.clip_top = mClip.fTop,
.clip_right = mClip.fRight,
.clip_bottom = mClip.fBottom,
+ .is_layer = !vulkan_info.fFromSwapchainOrAndroidWindow,
};
mat4.getColMajor(&params.transform[0]);
params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index 4ae0f5a0a2e5..5c596576df4e 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -68,6 +68,9 @@ struct VkFunctorDrawParams {
int clip_top;
int clip_right;
int clip_bottom;
+
+ // Input: Whether destination surface is offscreen surface.
+ bool is_layer;
};
} // namespace uirenderer
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index fe9a30a59870..611a4d9c8f2c 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -426,7 +426,7 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
if (bufferInfo->skSurface.get() == nullptr) {
bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
- kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
+ kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr, /*from_window=*/true);
if (bufferInfo->skSurface.get() == nullptr) {
ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 8f04cfb70469..f43586f8d9d0 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -17,24 +17,29 @@
#define LOG_TAG "PointerController"
//#define LOG_NDEBUG 0
-// Log debug messages about pointer updates
-#define DEBUG_POINTER_UPDATES 0
-
#include "PointerController.h"
-#include "MouseCursorController.h"
#include "PointerControllerContext.h"
-#include "TouchSpotController.h"
-
-#include <log/log.h>
-#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
-#include <SkPaint.h>
namespace android {
+namespace {
+
+const ui::Transform kIdentityTransform;
+
+} // namespace
+
+// --- PointerController::DisplayInfoListener ---
+
+void PointerController::DisplayInfoListener::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>& displayInfo) {
+ mPointerController.onDisplayInfosChanged(displayInfo);
+}
+
// --- PointerController ---
std::shared_ptr<PointerController> PointerController::create(
@@ -63,9 +68,16 @@ std::shared_ptr<PointerController> PointerController::create(
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper,
const sp<SpriteController>& spriteController)
- : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+ : mContext(policy, looper, spriteController, *this),
+ mCursorController(mContext),
+ mDisplayInfoListener(new DisplayInfoListener(*this)) {
std::scoped_lock lock(mLock);
mLocked.presentation = Presentation::SPOT;
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
+}
+
+PointerController::~PointerController() {
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mDisplayInfoListener);
}
bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -74,7 +86,14 @@ bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX
}
void PointerController::move(float deltaX, float deltaY) {
- mCursorController.move(deltaX, deltaY);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+ }
+ mCursorController.move(transformed.x, transformed.y);
}
void PointerController::setButtonState(int32_t buttonState) {
@@ -86,12 +105,26 @@ int32_t PointerController::getButtonState() const {
}
void PointerController::setPosition(float x, float y) {
- std::scoped_lock lock(mLock);
- mCursorController.setPosition(x, y);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transform.transform(x, y);
+ }
+ mCursorController.setPosition(transformed.x, transformed.y);
}
void PointerController::getPosition(float* outX, float* outY) const {
+ const int32_t displayId = mCursorController.getDisplayId();
mCursorController.getPosition(outX, outY);
+ {
+ std::scoped_lock lock(mLock);
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ const auto xy = transform.inverse().transform(*outX, *outY);
+ *outX = xy.x;
+ *outY = xy.y;
+ }
}
int32_t PointerController::getDisplayId() const {
@@ -130,11 +163,25 @@ void PointerController::setPresentation(Presentation presentation) {
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
std::scoped_lock lock(mLock);
+ std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+ const ui::Transform& transform = getTransformForDisplayLocked(displayId);
+
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+
+ const vec2 xy = transform.transform(spotCoords[index].getXYValue());
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ }
+
auto it = mLocked.spotControllers.find(displayId);
if (it == mLocked.spotControllers.end()) {
mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
}
- mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
+ mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
}
void PointerController::clearSpots() {
@@ -194,7 +241,7 @@ void PointerController::doInactivityTimeout() {
void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
- for (DisplayViewport viewport : viewports) {
+ for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
}
@@ -214,4 +261,17 @@ void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>&
}
}
+void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
+ std::scoped_lock lock(mLock);
+ mLocked.mDisplayInfos = displayInfo;
+}
+
+const ui::Transform& PointerController::getTransformForDisplayLocked(int displayId) const {
+ const auto& di = mLocked.mDisplayInfos;
+ auto it = std::find_if(di.begin(), di.end(), [displayId](const gui::DisplayInfo& info) {
+ return info.displayId == displayId;
+ });
+ return it != di.end() ? it->transform : kIdentityTransform;
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 97567bab202b..796077f6c38c 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@ public:
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
- virtual ~PointerController() = default;
+ ~PointerController() override;
virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
virtual void move(float deltaX, float deltaY);
@@ -72,6 +72,8 @@ public:
void reloadPointerResources();
void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+
private:
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -85,9 +87,23 @@ private:
struct Locked {
Presentation presentation;
+ std::vector<gui::DisplayInfo> mDisplayInfos;
std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
} mLocked GUARDED_BY(mLock);
+ class DisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>&) override;
+
+ private:
+ PointerController& mPointerController;
+ };
+ sp<DisplayInfoListener> mDisplayInfoListener;
+
+ const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
void clearSpotsLocked();
diff --git a/media/Android.bp b/media/Android.bp
index 15b24b220768..fcdfd72c91d5 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -115,6 +115,7 @@ aidl_interface {
aidl_interface {
name: "android.media.soundtrigger.types",
vendor_available: true,
+ host_supported: true,
flags: [
"-Werror",
"-Weverything",
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index c4f82ebc830f..2f165f0de7e5 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSessionCallback;
/**
@@ -24,5 +25,7 @@ import android.media.tv.interactive.ITvIAppSessionCallback;
* @hide
*/
oneway interface ITvIAppService {
+ void registerCallback(in ITvIAppServiceCallback callback);
+ void unregisterCallback(in ITvIAppServiceCallback callback);
void createSession(in ITvIAppSessionCallback callback, in String iAppServiceId, int type);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 8863729bfab3..78b8173e0af7 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
@@ -62,11 +63,26 @@ public abstract class TvIAppService extends Service {
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
private final Handler mServiceHandler = new ServiceHandler();
+ private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
+ new RemoteCallbackList<>();
/** @hide */
@Override
public final IBinder onBind(Intent intent) {
ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
+ @Override
+ public void registerCallback(ITvIAppServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.register(cb);
+ }
+ }
+
+ @Override
+ public void unregisterCallback(ITvIAppServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.unregister(cb);
+ }
+ }
@Override
public void createSession(ITvIAppSessionCallback cb, String iAppServiceId, int type) {
@@ -137,7 +153,7 @@ public abstract class TvIAppService extends Service {
* Called when the application sets the surface.
*
* <p>The TV IApp service should render interactive app UI onto the given surface. When
- * called with {@code null}, the input service should immediately free any references to the
+ * called with {@code null}, the IApp service should immediately free any references to the
* currently set surface and stop using it.
*
* @param surface The surface to be used for interactive app UI rendering. Can be
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index ae2711200afd..f0f576e90d91 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -251,8 +251,8 @@ public class Filter implements AutoCloseable {
private native int nativeFlushFilter();
private native int nativeRead(byte[] buffer, long offset, long size);
private native int nativeClose();
- private native String nativeCreateSharedFilter();
- private native void nativeReleaseSharedFilter(String token);
+ private native String nativeAcquireSharedFilterToken();
+ private native void nativeFreeSharedFilterToken(String token);
// Called by JNI
private Filter(long id) {
@@ -562,20 +562,20 @@ public class Filter implements AutoCloseable {
}
/**
- * Creates a shared filter.
+ * Acquires a shared filter token.
*
* @return a string shared filter token.
*/
@Nullable
- public String createSharedFilter() {
+ public String acquireSharedFilterToken() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsStarted || mIsShared) {
- Log.d(TAG, "Create shared filter in a wrong state, started: " +
+ Log.d(TAG, "Acquire shared filter in a wrong state, started: " +
mIsStarted + "shared: " + mIsShared);
return null;
}
- String token = nativeCreateSharedFilter();
+ String token = nativeAcquireSharedFilterToken();
if (token != null) {
mIsShared = true;
}
@@ -584,17 +584,17 @@ public class Filter implements AutoCloseable {
}
/**
- * Releases a shared filter.
+ * Frees a shared filter token.
*
* @param filterToken the token of the shared filter being released.
*/
- public void releaseSharedFilter(@NonNull String filterToken) {
+ public void freeSharedFilterToken(@NonNull String filterToken) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (!mIsShared) {
return;
}
- nativeReleaseSharedFilter(filterToken);
+ nativeFreeSharedFilterToken(filterToken);
mIsShared = false;
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index c4dfee42c3cd..9526c52e3250 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3890,22 +3890,22 @@ static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
return (jint)r;
}
-static jstring android_media_tv_Tuner_create_shared_filter(JNIEnv *env, jobject filter) {
+static jstring android_media_tv_Tuner_acquire_shared_filter_token(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to create shared filter: filter client not found");
+ "Failed to acquire shared filter token: filter client not found");
return nullptr;
}
- string token = filterClient->createSharedFilter();
+ string token = filterClient->acquireSharedFilterToken();
if (token.empty()) {
return nullptr;
}
return env->NewStringUTF(token.data());
}
-static void android_media_tv_Tuner_release_shared_filter(
+static void android_media_tv_Tuner_free_shared_filter_token(
JNIEnv *env, jobject filter, jstring token) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
@@ -3915,7 +3915,7 @@ static void android_media_tv_Tuner_release_shared_filter(
}
std::string filterToken(env->GetStringUTFChars(token, nullptr));
- filterClient->releaseSharedFilter(filterToken);
+ filterClient->freeSharedFilterToken(filterToken);
}
static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
@@ -4408,10 +4408,10 @@ static const JNINativeMethod gFilterMethods[] = {
{ "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
{ "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
- {"nativeCreateSharedFilter", "()Ljava/lang/String;",
- (void *)android_media_tv_Tuner_create_shared_filter},
- {"nativeReleaseSharedFilter", "(Ljava/lang/String;)V",
- (void *)android_media_tv_Tuner_release_shared_filter},
+ {"nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
+ (void *)android_media_tv_Tuner_acquire_shared_filter_token},
+ {"nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
+ (void *)android_media_tv_Tuner_free_shared_filter_token},
};
static const JNINativeMethod gSharedFilterMethods[] = {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index e8b3de82f284..bd283dce60a5 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -196,10 +196,10 @@ Result FilterClient::close() {
return Result::INVALID_STATE;
}
-string FilterClient::createSharedFilter() {
+string FilterClient::acquireSharedFilterToken() {
if (mTunerFilter != nullptr) {
string filterToken;
- if (mTunerFilter->createSharedFilter(&filterToken).isOk()) {
+ if (mTunerFilter->acquireSharedFilterToken(&filterToken).isOk()) {
return filterToken;
}
}
@@ -207,9 +207,9 @@ string FilterClient::createSharedFilter() {
return "";
}
-Result FilterClient::releaseSharedFilter(const string& filterToken) {
+Result FilterClient::freeSharedFilterToken(const string& filterToken) {
if (mTunerFilter != nullptr) {
- Status s = mTunerFilter->releaseSharedFilter(filterToken);
+ Status s = mTunerFilter->freeSharedFilterToken(filterToken);
return ClientHelper::getServiceSpecificErrorCode(s);
}
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index c031b2ac0b5b..7ebe7bc76c6f 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -143,14 +143,14 @@ public:
Result close();
/**
- * Create a new SharedFiler.
+ * Accquire a new SharedFiler token.
*/
- string createSharedFilter();
+ string acquireSharedFilterToken();
/**
- * Release SharedFiler.
+ * Release SharedFiler token.
*/
- Result releaseSharedFilter(const string& filterToken);
+ Result freeSharedFilterToken(const string& filterToken);
private:
Result getFilterMq();
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
new file mode 100644
index 000000000000..2b81200f0079
--- /dev/null
+++ b/omapi/aidl/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.se.omapi",
+ vendor_available: true,
+ srcs: ["android/se/omapi/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ separate_platform_variant: false,
+ },
+ },
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
new file mode 100644
index 000000000000..725013a35cde
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementChannel {
+ void close();
+ boolean isClosed();
+ boolean isBasicChannel();
+ byte[] getSelectResponse();
+ byte[] transmit(in byte[] command);
+ boolean selectNext();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
new file mode 100644
index 000000000000..77e1c53f47ac
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017, 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.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementListener {
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
new file mode 100644
index 000000000000..2b10c473c902
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017, 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.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementReader {
+ boolean isSecureElementPresent();
+ android.se.omapi.ISecureElementSession openSession();
+ void closeSessions();
+ boolean reset();
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
new file mode 100644
index 000000000000..ae6346278691
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementService {
+ String[] getReaders();
+ android.se.omapi.ISecureElementReader getReader(in String reader);
+ boolean[] isNFCEventAllowed(in String reader, in byte[] aid, in String[] packageNames);
+}
diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
new file mode 100644
index 000000000000..06287c551f5c
--- /dev/null
+++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *//*
+ * Copyright (c) 2015-2017, The Linux Foundation.
+ *//*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.se.omapi;
+/* @hide */
+@VintfStability
+interface ISecureElementSession {
+ byte[] getAtr();
+ void close();
+ void closeChannels();
+ boolean isClosed();
+ android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+ android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener);
+}
diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
index 4ae57ab829cb..bbd3c148caaf 100644
--- a/core/java/android/se/omapi/ISecureElementChannel.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl
@@ -22,6 +22,7 @@ package android.se.omapi;
import android.se.omapi.ISecureElementSession;
/** @hide */
+@VintfStability
interface ISecureElementChannel {
/**
@@ -58,6 +59,9 @@ interface ISecureElementChannel {
* Transmits the specified command APDU and returns the response APDU.
* MANAGE channel commands are not supported.
* Selection of applets is not supported in logical channels.
+ *
+ * @param command Command APDU, its structure is defined in ISO/IEC 7816-4
+ * in Standard byte format
*/
byte[] transmit(in byte[] command);
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
index e9dd18181c56..479dcd7d5acf 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl
@@ -23,5 +23,6 @@ package android.se.omapi;
* Interface to receive call-backs when the service is connected.
* @hide
*/
+@VintfStability
interface ISecureElementListener {
}
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
index 41244ab058e0..a6979face61f 100644
--- a/core/java/android/se/omapi/ISecureElementReader.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl
@@ -22,6 +22,7 @@ package android.se.omapi;
import android.se.omapi.ISecureElementSession;
/** @hide */
+@VintfStability
interface ISecureElementReader {
/**
@@ -34,7 +35,7 @@ interface ISecureElementReader {
* Connects to a secure element in this reader. <br>
* This method prepares (initialises) the Secure Element for communication
* before the Session object is returned (e.g. powers the Secure Element by
- * ICC ON if its not already on). There might be multiple sessions opened at
+ * ICC ON if it is not already on). There might be multiple sessions opened at
* the same time on the same reader. The system ensures the interleaving of
* APDUs between the respective sessions.
*
diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
index 4fa799e78757..61ae4816de82 100644
--- a/core/java/android/se/omapi/ISecureElementService.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementService.aidl
@@ -28,23 +28,31 @@ import android.se.omapi.ISecureElementReader;
* SecureElement service interface.
* @hide
*/
+@VintfStability
interface ISecureElementService {
/**
* Returns the friendly names of available Secure Element readers.
+ * <ul>
+ * <li>If the reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+ * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+ * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+ * </ul>
+ * Slot is a decimal number without leading zeros. The Numbering must start with 1
+ * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
*/
String[] getReaders();
/**
* Returns SecureElement Service reader object to the given name.
*/
- ISecureElementReader getReader(String reader);
+ ISecureElementReader getReader(in String reader);
/**
* Checks if the application defined by the package name is allowed to
* receive NFC transaction events for the defined AID.
*/
- boolean[] isNFCEventAllowed(String reader, in byte[] aid,
+ boolean[] isNFCEventAllowed(in String reader, in byte[] aid,
in String[] packageNames);
}
diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
index 8ea599f2e866..129ecc4ddaa3 100644
--- a/core/java/android/se/omapi/ISecureElementSession.aidl
+++ b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl
@@ -27,6 +27,7 @@ import android.se.omapi.ISecureElementReader;
import android.se.omapi.ISecureElementListener;
/** @hide */
+@VintfStability
interface ISecureElementSession {
/**
@@ -45,7 +46,6 @@ interface ISecureElementSession {
*/
void closeChannels();
-
/**
* Tells if this session is closed.
*
@@ -59,15 +59,19 @@ interface ISecureElementSession {
* applet if aid != null.
* Logical channels cannot be opened with this connection.
* Use interface method openLogicalChannel() to open a logical channel.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
*/
ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2,
- ISecureElementListener listener);
+ in ISecureElementListener listener);
/**
* Opens a connection using the next free logical channel of the card in the
* specified reader. Selects the specified applet.
* Selection of other applets with this connection is not supported.
+ * Listener is passed to secure element service and used to monitor whether
+ * the client application that uses OMAPI is still alive or not.
*/
ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2,
- ISecureElementListener listener);
+ in ISecureElementListener listener);
}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
new file mode 100644
index 000000000000..f03c3f6eb647
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeAccessControlTestCases",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeAccessControlTestCases.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
new file mode 100644
index 000000000000..9ea65431417a
--- /dev/null
+++ b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEAccessControlTest : public TestWithParam<std::string> {
+ protected:
+
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void testSelectableAid(
+ std::vector<std::vector<uint8_t>> authorizedAids) {
+ for (auto aid : authorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAid(
+ std::vector<std::vector<uint8_t>> unAuthorizedAids) {
+ for (auto aid : unAuthorizedAids) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ void testTransmitAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+ }
+ }
+ }
+
+ void testUnauthorisedAPDU(
+ std::vector<uint8_t> aid,
+ std::vector<std::vector<uint8_t>> apdus) {
+ for (auto apdu : apdus) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ ASSERT_NE(reader, nullptr) << "reader is null";
+ bool status = false;
+ std::vector<uint8_t> selectResponse = {};
+ std::vector<uint8_t> transmitResponse = {};
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ ASSERT_TRUE(
+ verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+
+ res = channel->transmit(apdu, &transmitResponse);
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ if (!res.isOk()) {
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ }
+ }
+ }
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ void getFirstApiLevel(int32_t* outApiLevel) {
+ int32_t firstApiLevel = property_get_int32(FEATURE_SE_API_LEVEL.c_str(), -1);
+ if (firstApiLevel < 0) {
+ firstApiLevel = property_get_int32(FEATURE_SE_SDK_VERSION.c_str(), -1);
+ }
+ ASSERT_GT(firstApiLevel, 0); // first_api_level must exist
+ *outApiLevel = firstApiLevel;
+ return;
+ }
+
+ bool supportsHardware() {
+ bool lowRamDevice = property_get_bool(FEATURE_SE_LOW_RAM.c_str(), true);
+ return !lowRamDevice || deviceSupportsFeature(FEATURE_SE_HARDWARE_WATCH.c_str()) ||
+ deviceSupportsFeature(FEATURE_SE_OMAPI_SERVICE.c_str()); // android.se.omapi
+ }
+
+ void SetUp() override {
+ ASSERT_TRUE(supportsHardware());
+ int32_t apiLevel;
+ getFirstApiLevel(&apiLevel);
+ ASSERT_TRUE(apiLevel > 27);
+ ASSERT_TRUE(supportOMAPIReaders());
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (mOmapiSeService != NULL) {
+ auto status = mOmapiSeService->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = mOmapiSeService->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+ static inline std::string const FEATURE_SE_LOW_RAM = "ro.config.low_ram";
+ static inline std::string const FEATURE_SE_HARDWARE_WATCH = "android.hardware.type.watch";
+ static inline std::string const FEATURE_SE_OMAPI_SERVICE = "com.android.se";
+ static inline std::string const FEATURE_SE_SDK_VERSION = "ro.build.version.sdk";
+ static inline std::string const FEATURE_SE_API_LEVEL = "ro.product.first_api_level";
+
+ std::vector<uint8_t> AID_40 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x40};
+ std::vector<uint8_t> AID_41 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x41};
+ std::vector<uint8_t> AID_42 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x42};
+ std::vector<uint8_t> AID_43 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x43};
+ std::vector<uint8_t> AID_44 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x44};
+ std::vector<uint8_t> AID_45 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x45};
+ std::vector<uint8_t> AID_46 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x46};
+ std::vector<uint8_t> AID_47 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x47};
+ std::vector<uint8_t> AID_48 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x48};
+ std::vector<uint8_t> AID_49 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x49};
+ std::vector<uint8_t> AID_4A = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4A};
+ std::vector<uint8_t> AID_4B = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4B};
+ std::vector<uint8_t> AID_4C = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4C};
+ std::vector<uint8_t> AID_4D = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4D};
+ std::vector<uint8_t> AID_4E = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4E};
+ std::vector<uint8_t> AID_4F = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4F};
+
+ std::vector<std::vector<uint8_t>> AUTHORIZED_AID = {AID_40, AID_41, AID_42, AID_44, AID_45,
+ AID_47, AID_48, AID_49, AID_4A, AID_4B,
+ AID_4C, AID_4D, AID_4E, AID_4F};
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_AID = {AID_43, AID_46};
+
+ /* Authorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ };
+ /* Unauthorized APDU for AID_40 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_40 = {
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00, 0x00},
+ };
+
+ /* Authorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_41 = {
+ {0x94, 0x06, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+ /* Unauthorized APDU for AID_41 */
+ std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_41 = {
+ {0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ };
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+};
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAID) {
+ testSelectableAid(AUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorizedAID) {
+ testUnauthorisedAid(UNAUTHORIZED_AID);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID40) {
+ testTransmitAPDU(AID_40, AUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID40) {
+ testUnauthorisedAPDU(AID_40, UNAUTHORIZED_APDU_AID_40);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID41) {
+ testTransmitAPDU(AID_41, AUTHORIZED_APDU_AID_41);
+}
+
+TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID41) {
+ testUnauthorisedAPDU(AID_41, UNAUTHORIZED_APDU_AID_41);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEAccessControlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEAccessControlTest);
+
+} // namespace
diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp
new file mode 100644
index 000000000000..c3ab8d13920c
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/Android.bp
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalOmapiSeServiceV1_TargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalOmapiSeServiceV1_TargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libnativehelper",
+ "libutils",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase",
+ "android.se.omapi-V1-ndk",
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+ require_root: true,
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
new file mode 100644
index 000000000000..319cb7e70884
--- /dev/null
+++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <VtsCoreUtil.h>
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/String16.h>
+
+using namespace std;
+using namespace ::testing;
+using namespace android;
+
+int main(int argc, char** argv) {
+ InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
+
+namespace {
+
+class OMAPISEServiceHalTest : public TestWithParam<std::string> {
+ protected:
+ class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+ void testSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid, std::vector<uint8_t>& selectResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+ }
+
+ void testNonSelectableAid(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> aid) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(aid, 0x00, seListener, &channel);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ LOG(ERROR) << res.getMessage();
+ ASSERT_FALSE(res.isOk()) << "expected to fail to open channel for this test";
+ }
+
+ /**
+ * Verifies TLV data
+ *
+ * @return true if the data is tlv formatted, false otherwise
+ */
+ bool verifyBerTlvData(std::vector<uint8_t> tlv) {
+ if (tlv.size() == 0) {
+ LOG(ERROR) << "Invalid tlv, null";
+ return false;
+ }
+ int i = 0;
+ if ((tlv[i++] & 0x1F) == 0x1F) {
+ // extra byte for TAG field
+ i++;
+ }
+
+ int len = tlv[i++] & 0xFF;
+ if (len > 127) {
+ // more than 1 byte for length
+ int bytesLength = len - 128;
+ len = 0;
+ for (int j = bytesLength; j > 0; j--) {
+ len += (len << 8) + (tlv[i++] & 0xFF);
+ }
+ }
+ // Additional 2 bytes for the SW
+ return (tlv.size() == (i + len + 2));
+ }
+
+ void internalTransmitApdu(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ res = channel->transmit(apdu, &transmitResponse);
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ ASSERT_TRUE(res.isOk()) << "failed to transmit";
+ }
+
+ bool supportOMAPIReaders() {
+ return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str()));
+ }
+
+ void SetUp() override {
+ LOG(INFO) << "get OMAPI service with name:" << GetParam();
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str()));
+ mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+ ASSERT_TRUE(mOmapiSeService);
+
+ std::vector<std::string> readers = {};
+
+ if (omapiSecureService() != NULL) {
+ auto status = omapiSecureService()->getReaders(&readers);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ for (auto readerName : readers) {
+ // Filter eSE readers only
+ if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = omapiSecureService()->getReader(readerName, &reader);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ mVSReaders[readerName] = reader;
+ }
+ }
+ }
+ }
+
+ void TearDown() override {
+ if (mOmapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ }
+ }
+ }
+
+ bool isDebuggableBuild() {
+ char value[PROPERTY_VALUE_MAX] = {0};
+ property_get("ro.system.build.type", value, "");
+ if (strcmp(value, "userdebug") == 0) {
+ return true;
+ }
+ if (strcmp(value, "eng") == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSecureService() {
+ return mOmapiSeService;
+ }
+
+ static inline std::string const ESE_READER_PREFIX = "eSE";
+ static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+ std::vector<uint8_t> SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31};
+ std::vector<uint8_t> LONG_SELECT_RESPONSE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
+ 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
+ 0x43, 0x54, 0x53, 0x32};
+ std::vector<uint8_t> NON_SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF};
+
+ std::vector<std::vector<uint8_t>> ILLEGAL_COMMANDS_TRANSMIT = {
+ {0x00, 0x70, 0x00, 0x00},
+ {0x00, 0x70, 0x80, 0x00},
+ {0x00, 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53, 0x52, 0x31, 0x37, 0x37,
+ 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> NO_DATA_APDU = {{0x00, 0x06, 0x00, 0x00},
+ {0x80, 0x06, 0x00, 0x00},
+ {0xA0, 0x06, 0x00, 0x00},
+ {0x94, 0x06, 0x00, 0x00},
+ {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA},
+ {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<std::vector<uint8_t>> DATA_APDU = {{0x00, 0x08, 0x00, 0x00, 0x00},
+ {0x80, 0x08, 0x00, 0x00, 0x00},
+ {0xA0, 0x08, 0x00, 0x00, 0x00},
+ {0x94, 0x08, 0x00, 0x00, 0x00},
+ {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00},
+ {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}};
+
+ /* Case 2 APDU command expects the P2 received in the SELECT command as 1-byte outgoing data */
+ std::vector<uint8_t> CHECK_SELECT_P2_APDU = {0x00, 0xF4, 0x00, 0x00, 0x00};
+
+ /* OMAPI APDU Test case 1 and 3 */
+ std::vector<std::vector<uint8_t>> SW_62xx_NO_DATA_APDU = {{0x00, 0xF3, 0x00, 0x06},
+ {0x00, 0xF3, 0x00, 0x0A, 0x01, 0xAA}};
+
+ /* OMAPI APDU Test case 2 and 4 */
+ std::vector<uint8_t> SW_62xx_DATA_APDU = {0x00, 0xF3, 0x00, 0x08, 0x00};
+ std::vector<uint8_t> SW_62xx_VALIDATE_DATA_APDU = {0x00, 0xF3, 0x00, 0x0C, 0x01, 0xAA, 0x00};
+ std::vector<std::vector<uint8_t>> SW_62xx = {
+ {0x62, 0x00}, {0x62, 0x81}, {0x62, 0x82}, {0x62, 0x83}, {0x62, 0x85}, {0x62, 0xF1},
+ {0x62, 0xF2}, {0x63, 0xF1}, {0x63, 0xF2}, {0x63, 0xC2}, {0x62, 0x02}, {0x62, 0x80},
+ {0x62, 0x84}, {0x62, 0x86}, {0x63, 0x00}, {0x63, 0x81}};
+
+ std::vector<std::vector<uint8_t>> SEGMENTED_RESP_APDU = {
+ // Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC2, 0x08, 0x00, 0x00},
+ // Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC6, 0x08, 0x00, 0x00},
+ // Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00},
+ // Test device buffer capacity 7FFF data
+ {0x00, 0xC2, 0x7F, 0xFF, 0x00},
+ // Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x00, 0xCF, 0x08, 0x00, 0x00},
+ // Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes
+ {0x94, 0xC2, 0x08, 0x00, 0x00}};
+ long SERVICE_CONNECTION_TIME_OUT = 3000;
+
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService;
+
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders = {};
+};
+
+/** Tests getReaders API */
+TEST_P(OMAPISEServiceHalTest, TestGetReaders) {
+ std::vector<std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> eseReaders =
+ {};
+
+ for (const auto& [name, reader] : mVSReaders) {
+ bool status = false;
+ LOG(INFO) << "Name of the reader: " << name;
+
+ if (reader) {
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ }
+ ASSERT_TRUE(status);
+
+ if (name.find(ESE_READER_PREFIX) == std::string::npos) {
+ LOG(ERROR) << "Incorrect Reader name";
+ FAIL();
+ }
+
+ if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ eseReaders.push_back(reader);
+ } else {
+ LOG(INFO) << "Reader not supported: " << name;
+ FAIL();
+ }
+ }
+
+ if (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())) {
+ ASSERT_GE(eseReaders.size(), 1);
+ } else {
+ ASSERT_TRUE(eseReaders.size() == 0);
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is null */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ std::vector<uint8_t> aid = {};
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(aid, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests OpenBasicChannel API when aid is provided */
+TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNonNullAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ bool result = false;
+
+ auto status = reader->openSession(&session);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+ if (!session) {
+ LOG(ERROR) << "Could not open session";
+ FAIL();
+ }
+
+ status = session->openBasicChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+
+ if (channel != nullptr) {
+ status = channel->isBasicChannel(&result);
+ ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened";
+ }
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestSelectableAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, SELECTABLE_AID, selectResponse);
+ }
+ }
+}
+
+/** Tests Select API */
+TEST_P(OMAPISEServiceHalTest, TestLongSelectResponse) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> selectResponse = {};
+ testSelectableAid(reader, LONG_SELECT_RESPONSE_AID, selectResponse);
+ ASSERT_TRUE(verifyBerTlvData(selectResponse)) << "Select Response is not complete";
+ }
+ }
+}
+
+/** Test to fail open channel with wrong aid */
+TEST_P(OMAPISEServiceHalTest, TestWrongAid) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ testNonSelectableAid(reader, NON_SELECTABLE_AID);
+ }
+ }
+}
+
+/** Tests with invalid cmds in Transmit */
+TEST_P(OMAPISEServiceHalTest, TestSecurityExceptionInTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>();
+ std::vector<uint8_t> selectResponse = {};
+
+ ASSERT_NE(reader, nullptr) << "reader is null";
+
+ bool status = false;
+ auto res = reader->isSecureElementPresent(&status);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_TRUE(status);
+
+ res = reader->openSession(&session);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(session, nullptr) << "Could not open session";
+
+ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel);
+ ASSERT_TRUE(res.isOk()) << res.getMessage();
+ ASSERT_NE(channel, nullptr) << "Could not open channel";
+
+ res = channel->getSelectResponse(&selectResponse);
+ ASSERT_TRUE(res.isOk()) << "failed to get Select Response";
+ ASSERT_GE(selectResponse.size(), 2);
+
+ ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90));
+
+ for (auto cmd : ILLEGAL_COMMANDS_TRANSMIT) {
+ std::vector<uint8_t> response = {};
+ res = channel->transmit(cmd, &response);
+ ASSERT_EQ(res.getExceptionCode(), EX_SECURITY);
+ ASSERT_FALSE(res.isOk()) << "expected failed status for this test";
+ }
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ }
+ }
+}
+
+/**
+ * Tests Transmit API for all readers.
+ *
+ * Checks the return status and verifies the size of the
+ * response.
+ */
+TEST_P(OMAPISEServiceHalTest, TestTransmitApdu) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : NO_DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+
+ for (auto apdu : DATA_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ /* 256 byte data and 2 bytes of status word */
+ ASSERT_GE(response.size(), 258);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ }
+ }
+ }
+}
+
+/**
+ * Tests if underlying implementations returns the correct Status Word
+ *
+ * TO verify that :
+ * - the device does not modify the APDU sent to the Secure Element
+ * - the warning code is properly received by the application layer as SW answer
+ * - the verify that the application layer can fetch the additionnal data (when present)
+ */
+TEST_P(OMAPISEServiceHalTest, testStatusWordTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SW_62xx_NO_DATA_APDU) {
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 2);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+
+ for (uint8_t i = 0x00; i < SW_62xx.size(); i++) {
+ std::vector<uint8_t> apdu = SW_62xx_VALIDATE_DATA_APDU;
+ apdu[2] = i + 1;
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ ASSERT_GE(response.size(), apdu.size() + 2);
+ std::vector<uint8_t> responseSubstring((response.begin() + 0),
+ (response.begin() + apdu.size()));
+ // We should not care about which channel number is actually assigned.
+ responseSubstring[0] = apdu[0];
+ ASSERT_TRUE((responseSubstring == apdu));
+ std::vector<uint8_t> SW = SW_62xx[i];
+ ASSERT_EQ(response[response.size() - 1], SW[1]);
+ ASSERT_EQ(response[response.size() - 2], SW[0]);
+ }
+ }
+ }
+}
+
+/** Test if the responses are segmented by the underlying implementation */
+TEST_P(OMAPISEServiceHalTest, TestSegmentedResponseTransmit) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ for (auto apdu : SEGMENTED_RESP_APDU) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, apdu, response);
+ int expectedLength = (0x00 << 24) | (0x00 << 16) | (apdu[2] << 8) | apdu[3];
+ ASSERT_EQ(response.size(), (expectedLength + 2));
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0xFF));
+ }
+ }
+ }
+}
+
+/**
+ * Tests the P2 value of the select command.
+ *
+ * Verifies that the default P2 value (0x00) is not modified by the underlying implementation.
+ */
+TEST_P(OMAPISEServiceHalTest, TestP2Value) {
+ ASSERT_TRUE(supportOMAPIReaders() == true);
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ std::vector<uint8_t> response = {};
+ internalTransmitApdu(reader, CHECK_SELECT_P2_APDU, response);
+ ASSERT_GE(response.size(), 3);
+ ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00));
+ ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90));
+ ASSERT_EQ((response[response.size() - 3] & 0xFF), (0x00));
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEServiceHalTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(
+ aidl::android::se::omapi::ISecureElementService::descriptor)),
+ android::hardware::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEServiceHalTest);
+
+} // namespace
diff --git a/omapi/java/Android.bp b/omapi/java/Android.bp
new file mode 100644
index 000000000000..8d38da048d9b
--- /dev/null
+++ b/omapi/java/Android.bp
@@ -0,0 +1,17 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-omapi-sources",
+ srcs: [
+ "**/*.java",
+ "**/*.aidl",
+ ],
+ visibility: ["//frameworks/base"],
+}
diff --git a/core/java/android/se/OWNERS b/omapi/java/android/se/OWNERS
index 5682fd3281f4..5682fd3281f4 100644
--- a/core/java/android/se/OWNERS
+++ b/omapi/java/android/se/OWNERS
diff --git a/core/java/android/se/omapi/Channel.java b/omapi/java/android/se/omapi/Channel.java
index 90ce11ae0313..90ce11ae0313 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/omapi/java/android/se/omapi/Channel.java
diff --git a/core/java/android/se/omapi/OWNERS b/omapi/java/android/se/omapi/OWNERS
index 5682fd3281f4..5682fd3281f4 100644
--- a/core/java/android/se/omapi/OWNERS
+++ b/omapi/java/android/se/omapi/OWNERS
diff --git a/core/java/android/se/omapi/Reader.java b/omapi/java/android/se/omapi/Reader.java
index 90c934d189fa..3c2135d9bc9d 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/omapi/java/android/se/omapi/Reader.java
@@ -170,7 +170,9 @@ public final class Reader {
try {
closeSessions();
return mReader.reset();
- } catch (RemoteException ignore) {return false;}
+ } catch (RemoteException ignore) {
+ return false;
+ }
}
}
}
diff --git a/core/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
index 333af91ac872..f42ca364b6d9 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -230,20 +230,20 @@ public final class SEService {
* is not exist.
* @return A Reader object for this uicc slot.
*/
- public @NonNull Reader getUiccReader(int slotNumber) {
- if (slotNumber < 1) {
- throw new IllegalArgumentException("slotNumber should be larger than 0");
- }
- loadReaders();
+ public @NonNull Reader getUiccReader(int slotNumber) {
+ if (slotNumber < 1) {
+ throw new IllegalArgumentException("slotNumber should be larger than 0");
+ }
+ loadReaders();
- String readerName = UICC_TERMINAL + slotNumber;
- Reader reader = mReaders.get(readerName);
+ String readerName = UICC_TERMINAL + slotNumber;
+ Reader reader = mReaders.get(readerName);
- if (reader == null) {
+ if (reader == null) {
throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist");
- }
+ }
- return reader;
+ return reader;
}
/**
diff --git a/core/java/android/se/omapi/Session.java b/omapi/java/android/se/omapi/Session.java
index d5f8c82bf47e..d5f8c82bf47e 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/omapi/java/android/se/omapi/Session.java
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 58d2185ea9e9..1df1bce014cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -160,10 +160,12 @@ public class BluetoothEventManager {
private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) {
if (mUserHandle == null) {
// If userHandle has not been provided, simply call registerReceiver.
- mContext.registerReceiver(receiver, filter, null, mReceiverHandler);
+ mContext.registerReceiver(receiver, filter, null, mReceiverHandler,
+ Context.RECEIVER_EXPORTED);
} else {
// userHandle was explicitly specified, so need to call multi-user aware API.
- mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler);
+ mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler,
+ Context.RECEIVER_EXPORTED);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index a210e90a3cfc..8b17be1e8ec9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -19,7 +19,6 @@ package com.android.settingslib.notification;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -85,6 +84,7 @@ public class EnableZenModeDialog {
@VisibleForTesting
protected Context mContext;
+ private final int mThemeResId;
@VisibleForTesting
protected TextView mZenAlarmWarning;
@VisibleForTesting
@@ -97,10 +97,15 @@ public class EnableZenModeDialog {
protected LayoutInflater mLayoutInflater;
public EnableZenModeDialog(Context context) {
+ this(context, 0);
+ }
+
+ public EnableZenModeDialog(Context context, int themeResId) {
mContext = context;
+ mThemeResId = themeResId;
}
- public Dialog createDialog() {
+ public AlertDialog createDialog() {
mNotificationManager = (NotificationManager) mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
mForeverId = Condition.newId(mContext).appendPath("forever").build();
@@ -108,7 +113,7 @@ public class EnableZenModeDialog {
mUserId = mContext.getUserId();
mAttached = false;
- final AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId)
.setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 0fe4efefc2cb..a944bf58d1f5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -80,6 +80,7 @@ public class SystemSettings {
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
Settings.System.DISPLAY_COLOR_MODE,
Settings.System.ALARM_ALERT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 462c3a5bba03..63acffba662f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -120,6 +120,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.RINGTONE, URI_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR);
VALIDATORS.put(System.ALARM_ALERT, URI_VALIDATOR);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 3ccf5e4fbdd0..5fec4cccda6c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -53,8 +53,12 @@ open class GhostedViewLaunchAnimatorController(
private val ghostedView: View,
/** The [InteractionJankMonitor.CujType] associated to this animation. */
- private val cujType: Int? = null
+ private val cujType: Int? = null,
+ private var interactionJankMonitor: InteractionJankMonitor? = null
) : ActivityLaunchAnimator.Controller {
+
+ constructor(view: View, type: Int) : this(view, type, null)
+
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
@@ -170,7 +174,7 @@ open class GhostedViewLaunchAnimatorController(
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
- cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) }
+ cujType?.let { interactionJankMonitor?.begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -251,7 +255,7 @@ open class GhostedViewLaunchAnimatorController(
return
}
- cujType?.let { InteractionJankMonitor.getInstance().end(it) }
+ cujType?.let { interactionJankMonitor?.end(it) }
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index 0dec981bd4ae..991dc63ef830 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -35,7 +35,7 @@
android:id="@+id/volume_number"
android:layout_width="@dimen/tv_volume_dialog_bubble_size"
android:layout_height="@dimen/tv_volume_dialog_bubble_size"
- android:maxLength="2"
+ android:maxLength="3"
android:gravity="center"
android:fontFeatureSettings="tnum"
android:background="@drawable/tv_volume_dialog_circle"
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 86e2661f9534..98518c2bbf97 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -156,6 +156,14 @@
style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
+ <View
+ android:id="@+id/mobile_toggle_divider"
+ android:layout_width="1dp"
+ android:layout_height="28dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_vertical"
+ android:background="?android:attr/textColorSecondary"/>
+
<FrameLayout
android:layout_width="@dimen/settingslib_switch_track_width"
android:layout_height="48dp"
@@ -367,8 +375,9 @@
android:id="@+id/done_layout"
android:layout_width="67dp"
android:layout_height="48dp"
+ android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
- android:layout_marginBottom="40dp"
+ android:layout_marginBottom="34dp"
android:layout_gravity="end|center_vertical"
android:clickable="true"
android:focusable="true">
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 07e28b6d7f20..cb34218fd2fb 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -53,13 +53,17 @@
<style name="TextAppearance.InternetDialog.Active">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/connected_network_primary_color</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary.Active">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/connected_network_secondary_color</item>
+ <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active">
+ <item name="android:background">?android:attr/textColorSecondaryInverse</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d972b7fc25a6..dd67961343fd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -957,4 +957,10 @@
<style name="TextAppearance.InternetDialog.Secondary.Active"/>
+ <style name="InternetDialog.Divider">
+ <item name="android:background">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="InternetDialog.Divider.Active"/>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8bd0f910dac3..01497516e0b1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -95,4 +95,9 @@ oneway interface IOverviewProxy {
* Sent when screen turned on and ready to use (blocker scrim is hidden)
*/
void onScreenTurnedOn() = 21;
+
+ /**
+ * Sent when the desired dark intensity of the nav buttons has changed
+ */
+ void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 6154d84d5b37..8d98a7540a05 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,7 +18,6 @@ package com.android.systemui.shared.recents.utilities;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import android.annotation.TargetApi;
import android.content.Context;
@@ -126,10 +125,9 @@ public class Utilities {
final WindowManager windowManager = context.getSystemService(WindowManager.class);
final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- float originalSmallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+ float smallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
context.getResources().getConfiguration().densityDpi);
- return dpiFromPx(Math.min(bounds.width(), bounds.height()), DENSITY_DEVICE_STABLE)
- >= TABLET_MIN_DPS && originalSmallestWidth >= TABLET_MIN_DPS;
+ return smallestWidth >= TABLET_MIN_DPS;
}
public static float dpiFromPx(float size, int densityDpi) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e24f07c21076..b56d189d3ab3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -801,13 +801,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT);
}
+ boolean lockedOutStateChanged = false;
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged |= !mFingerprintLockedOut;
mFingerprintLockedOut = true;
if (isUdfpsEnrolled()) {
updateFingerprintListeningState();
@@ -820,9 +823,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void handleFingerprintLockoutReset() {
+ boolean changed = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
mFingerprintLockedOut = false;
mFingerprintLockedOutPermanent = false;
@@ -837,6 +845,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
} else {
updateFingerprintListeningState();
}
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FINGERPRINT);
+ }
}
private void setFingerprintRunningState(int fingerprintRunningState) {
@@ -999,7 +1011,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ boolean lockedOutStateChanged = false;
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
requireStrongAuthIfAllLockedOut();
}
@@ -1011,11 +1025,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
BiometricSourceType.FACE);
}
}
+
+ if (lockedOutStateChanged) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void handleFaceLockoutReset() {
+ boolean changed = mFaceLockedOutPermanent;
mFaceLockedOutPermanent = false;
+
updateFaceListeningState();
+
+ if (changed) {
+ notifyLockedOutStateChanged(BiometricSourceType.FACE);
+ }
}
private void setFaceRunningState(int faceRunningState) {
@@ -1237,6 +1261,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ private void notifyLockedOutStateChanged(BiometricSourceType type) {
+ Assert.isMainThread();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onLockedOutStateChanged(type);
+ }
+ }
+ }
+
public boolean isScreenOn() {
return mScreenOn;
}
@@ -2454,6 +2488,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
+ public boolean isFingerprintLockedOut() {
+ return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ }
+
/**
* If biometrics hardware is available, not disabled, and user has enrolled templates.
* This does NOT check if the device is encrypted or in lockdown.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12431984c9b9..8170a81a09e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -292,6 +292,11 @@ public class KeyguardUpdateMonitorCallback {
public void onStrongAuthStateChanged(int userId) { }
/**
+ * When the current user's locked out state changed.
+ */
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) { }
+
+ /**
* Called when the dream's window state is changed.
* @param dreaming true if the dream's window has been created and is visible
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 223eb78044c4..8f4d6f6aa973 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -255,7 +255,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
- mKeyguardViewManager.resetAlternateAuth(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
index 99c311e37217..53586f58c5d2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
@@ -23,6 +23,8 @@ import com.android.systemui.util.ViewController;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Optional;
+
/**
* {@link CommunalSource} defines an interface for working with a source for communal data. Clients
* may request a communal surface that can be shown within a {@link android.view.SurfaceView}.
@@ -30,6 +32,28 @@ import com.google.common.util.concurrent.ListenableFuture;
*/
public interface CommunalSource {
/**
+ * {@link Connector} defines an interface for {@link CommunalSource} instances to be generated.
+ */
+ interface Connector {
+ ListenableFuture<Optional<CommunalSource>> connect();
+ }
+
+ /**
+ * The {@link Observer} interface specifies an entity which {@link CommunalSource} listeners
+ * can be informed of changes to the source, which will require updating. Note that this deals
+ * with changes to the source itself, not content which will be updated through the
+ * {@link CommunalSource} interface.
+ */
+ interface Observer {
+ interface Callback {
+ void onSourceChanged();
+ }
+
+ void addCallback(Callback callback);
+ void removeCallback(Callback callback);
+ }
+
+ /**
* {@link CommunalViewResult} is handed back from {@link #requestCommunalView(Context)} and
* contains the view to be displayed and its associated controller.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
new file mode 100644
index 000000000000..3c2b79e25467
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link CommunalSourcePrimer} is responsible for priming SystemUI with a pre-configured
+ * Communal source. The SystemUI service binds to the component to retrieve the
+ * {@link CommunalSource}. {@link CommunalSourcePrimer} has no effect
+ * if there is no pre-defined value.
+ */
+@SysUISingleton
+public class CommunalSourcePrimer extends CoreStartable {
+ private static final String TAG = "CommunalSourcePrimer";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final DelayableExecutor mMainExecutor;
+ private final CommunalSourceMonitor mMonitor;
+ private final int mBaseReconnectDelayMs;
+ private final int mMaxReconnectAttempts;
+
+ private int mReconnectAttempts = 0;
+ private Runnable mCurrentReconnectCancelable;
+ private ListenableFuture<Optional<CommunalSource>> mGetSourceFuture;
+
+ private final Optional<CommunalSource.Connector> mConnector;
+ private final Optional<CommunalSource.Observer> mObserver;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCurrentReconnectCancelable = null;
+ connect();
+ }
+ };
+
+ @Inject
+ public CommunalSourcePrimer(Context context, @Main Resources resources,
+ DelayableExecutor mainExecutor,
+ CommunalSourceMonitor monitor,
+ Optional<CommunalSource.Connector> connector,
+ Optional<CommunalSource.Observer> observer) {
+ super(context);
+ mMainExecutor = mainExecutor;
+ mMonitor = monitor;
+ mConnector = connector;
+ mObserver = observer;
+
+ mMaxReconnectAttempts = resources.getInteger(
+ R.integer.config_communalSourceMaxReconnectAttempts);
+ mBaseReconnectDelayMs = resources.getInteger(
+ R.integer.config_communalSourceReconnectBaseDelay);
+ }
+
+ @Override
+ public void start() {
+ }
+
+ private void initiateConnectionAttempt() {
+ // Reset attempts
+ mReconnectAttempts = 0;
+ mMonitor.setSource(null);
+
+ // The first attempt is always a direct invocation rather than delayed.
+ connect();
+ }
+
+ private void scheduleConnectionAttempt() {
+ // always clear cancelable if present.
+ if (mCurrentReconnectCancelable != null) {
+ mCurrentReconnectCancelable.run();
+ mCurrentReconnectCancelable = null;
+ }
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ if (DEBUG) {
+ Log.d(TAG, "exceeded max connection attempts.");
+ }
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
+ }
+
+ mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+ reconnectDelayMs);
+
+ mReconnectAttempts++;
+ }
+
+ @Override
+ protected void onBootCompleted() {
+ if (mObserver.isPresent()) {
+ mObserver.get().addCallback(() -> initiateConnectionAttempt());
+ }
+ initiateConnectionAttempt();
+ }
+
+ private void connect() {
+ if (DEBUG) {
+ Log.d(TAG, "attempting to communal to communal source");
+ }
+
+ if (mGetSourceFuture != null) {
+ if (DEBUG) {
+ Log.d(TAG, "canceling in-flight connection");
+ }
+ mGetSourceFuture.cancel(true);
+ }
+
+ mGetSourceFuture = mConnector.get().connect();
+ mGetSourceFuture.addListener(() -> {
+ try {
+ Optional<CommunalSource> result = mGetSourceFuture.get();
+ if (result.isPresent()) {
+ final CommunalSource source = result.get();
+ source.addCallback(() -> initiateConnectionAttempt());
+ mMonitor.setSource(source);
+ } else {
+ scheduleConnectionAttempt();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }, mMainExecutor);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e97e7622c272..fbc9ba605000 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -793,7 +793,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
} else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
+ } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
+ || mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
@@ -820,6 +821,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mWallpaperSupportsAmbientMode;
/**
@@ -845,7 +847,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthController) {
+ Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ InteractionJankMonitor interactionJankMonitor) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -882,6 +885,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardStateController = keyguardStateController;
mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ mInteractionJankMonitor = interactionJankMonitor;
}
public void userActivity() {
@@ -2245,8 +2249,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
onKeyguardExitFinished();
mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
0 /* fadeoutDuration */);
- InteractionJankMonitor.getInstance()
- .end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@Override
@@ -2255,7 +2258,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
};
try {
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RunRemoteAnimation"));
runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
wallpapers, nonApps, callback);
@@ -2271,14 +2274,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
mSurfaceBehindRemoteAnimationRunning = true;
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("DismissPanel"));
// Pass the surface and metadata to the unlock animation controller.
mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
- InteractionJankMonitor.getInstance().begin(
+ mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RemoteAnimationDisabled"));
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
@@ -2288,7 +2291,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// supported, so it's always null.
mContext.getMainExecutor().execute(() -> {
if (finishedCallback == null) {
- InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
return;
}
@@ -2316,8 +2319,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
} finally {
- InteractionJankMonitor.getInstance()
- .end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
}
@@ -2328,8 +2330,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
} finally {
- InteractionJankMonitor.getInstance()
- .cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index cae9feeb62eb..8d23e9f6a12b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -97,7 +98,8 @@ public class KeyguardModule {
KeyguardStateController keyguardStateController,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Lazy<NotificationShadeDepthController> notificationShadeDepthController) {
+ Lazy<NotificationShadeDepthController> notificationShadeDepthController,
+ InteractionJankMonitor interactionJankMonitor) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -120,7 +122,8 @@ public class KeyguardModule {
keyguardStateController,
keyguardUnlockAnimationController,
unlockedScreenOffAnimationController,
- notificationShadeDepthController
+ notificationShadeDepthController,
+ interactionJankMonitor
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index e87558ebee27..47ef5e4c62fd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -833,12 +833,15 @@ internal object MediaPlayerData {
)
private val comparator =
- compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
- .thenByDescending { it.data.isPlaying }
- .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
- .thenByDescending { !it.data.resumption }
- .thenByDescending { it.updateTime }
- .thenByDescending { !it.data.isLocalSession }
+ compareByDescending<MediaSortKey> { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_LOCAL }
+ .thenByDescending { it.data.isPlaying == true &&
+ it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL }
+ .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
+ .thenByDescending { !it.data.resumption }
+ .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.updateTime }
+ .thenByDescending { it.data.notificationKey }
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6f0c88710461..bda07f428218 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -82,9 +82,9 @@ data class MediaData(
*/
var resumeAction: Runnable?,
/**
- * Local or remote playback
+ * Playback location: one of PLAYBACK_LOCAL, PLAYBACK_CAST_LOCAL, or PLAYBACK_CAST_REMOTE
*/
- var isLocalSession: Boolean = true,
+ var playbackLocation: Int = PLAYBACK_LOCAL,
/**
* Indicates that this player is a resumption player (ie. It only shows a play actions which
* will start the app and start playing).
@@ -110,7 +110,20 @@ data class MediaData(
* Timestamp when this player was last active.
*/
var lastActive: Long = 0L
-)
+) {
+ companion object {
+ /** Media is playing on the local device */
+ const val PLAYBACK_LOCAL = 0
+ /** Media is cast but originated on the local device */
+ const val PLAYBACK_CAST_LOCAL = 1
+ /** Media is from a remote cast notification */
+ const val PLAYBACK_CAST_REMOTE = 2
+ }
+
+ fun isLocalSession(): Boolean {
+ return playbackLocation == PLAYBACK_LOCAL
+ }
+}
/** State of a media action. */
data class MediaAction(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index c9b6d9c50e7a..6e86bef40181 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -27,6 +27,8 @@ import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
@@ -145,6 +147,24 @@ class MediaDataManager(
private var smartspaceSession: SmartspaceSession? = null
private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ /**
+ * Check whether this notification is an RCN
+ * TODO(b/204910409) implement new API for explicitly declaring this
+ */
+ private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
+ val pm = context.packageManager
+ try {
+ val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
+ if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
+ val extras = sbn.notification.extras
+ if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
+ return true
+ }
+ }
+ } catch (e: PackageManager.NameNotFoundException) { }
+ return false
+ }
+
@Inject
constructor(
context: Context,
@@ -434,7 +454,7 @@ class MediaDataManager(
val existed = mediaEntries[key] != null
backgroundExecutor.execute {
mediaEntries[key]?.let { mediaData ->
- if (mediaData.isLocalSession) {
+ if (mediaData.isLocalSession()) {
mediaData.token?.let {
val mediaController = mediaControllerFactory.create(it)
mediaController.transportControls.stop()
@@ -618,8 +638,11 @@ class MediaDataManager(
}
}
- val isLocalSession = mediaController.playbackInfo?.playbackType ==
- MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
+ val playbackLocation =
+ if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
+ else if (mediaController.playbackInfo?.playbackType ==
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
+ else MediaData.PLAYBACK_CAST_LOCAL
val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
val lastActive = systemClock.elapsedRealtime()
foregroundExecutor.execute {
@@ -629,7 +652,7 @@ class MediaDataManager(
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
- active, resumeAction = resumeAction, isLocalSession = isLocalSession,
+ active, resumeAction = resumeAction, playbackLocation = playbackLocation,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
isPlaying = isPlaying, isClearable = sbn.isClearable(),
lastActive = lastActive))
@@ -754,7 +777,7 @@ class MediaDataManager(
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
val removed = mediaEntries.remove(key)
- if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) {
+ if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession()) {
Log.d(TAG, "Not removing $key because resumable")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 608c784f5d39..d8095f3179b3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -193,7 +193,7 @@ class MediaResumeListener @Inject constructor(
mediaBrowser = null
}
// If we don't have a resume action, check if we haven't already
- if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) {
+ if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) {
// TODO also check for a media button receiver intended for restarting (b/154127084)
Log.d(TAG, "Checking for service component for " + data.packageName)
val pm = context.packageManager
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
new file mode 100644
index 000000000000..52103d3bd739
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them
+ * experience the joys of friendship.
+ * The events are then passed through
+ *
+ * Currently consolidates
+ * * A11y
+ * * Assistant
+ */
+@SysUISingleton
+public final class NavBarHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ Dumpable {
+ private final AccessibilityManager mAccessibilityManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final UserTracker mUserTracker;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
+ private int mNavBarMode;
+
+ private final ContentObserver mAssistContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssitantAvailability();
+ }
+ };
+
+ @Inject
+ public NavBarHelper(AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ OverviewProxyService overviewProxyService,
+ Lazy<AssistManager> assistManagerLazy,
+ NavigationModeController navigationModeController,
+ UserTracker userTracker,
+ DumpManager dumpManager) {
+ mAccessibilityManager = accessibilityManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mUserTracker = userTracker;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ mNavBarMode = navigationModeController.addListener(this);
+ overviewProxyService.addCallback(this);
+ dumpManager.registerDumpable(this);
+ }
+
+ public void init(Context context) {
+ mContext = context;
+ mContentResolver = mContext.getContentResolver();
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
+ false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssitantAvailability();
+ }
+
+ public void destroy() {
+ mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ }
+
+ /**
+ * @param listener Will immediately get callbacks based on current state
+ */
+ public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.add(listener);
+ listener.updateAccessibilityServicesState();
+ listener.updateAssistantAvailable(mAssistantAvailable);
+ }
+
+ public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchA11yEventUpdate() {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ private void dispatchAssistantEventUpdate(boolean assistantAvailable) {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAssistantAvailable(assistantAvailable);
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateAssitantAvailability();
+ }
+ }
+
+ private void updateAssitantAvailability() {
+ boolean assistantAvailableForUser = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ boolean longPressDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+ boolean gestureDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+
+ mAssistantAvailable = assistantAvailableForUser
+ && mAssistantTouchGestureEnabled
+ && QuickStepContract.isGesturalMode(mNavBarMode);
+ dispatchAssistantEventUpdate(mAssistantAvailable);
+ }
+
+ public boolean getLongPressHomeEnabled() {
+ return mLongPressHomeEnabled;
+ }
+
+ @Override
+ public void startAssistant(Bundle bundle) {
+ mAssistManagerLazy.get().startAssist(bundle);
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ updateAssitantAvailability();
+ }
+
+ /**
+ * Callbacks will get fired once immediately after registering via
+ * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
+ */
+ public interface NavbarTaskbarStateUpdater {
+ void updateAccessibilityServicesState();
+ void updateAssistantAvailable(boolean available);
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("NavbarTaskbarFriendster");
+ pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
+ pw.println(" mAssistantAvailable=" + mAssistantAvailable);
+ pw.println(" mNavBarMode=" + mNavBarMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index bdacc4118442..e0da9a0dca2c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -67,22 +67,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
@@ -200,8 +196,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
+ private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
@@ -213,9 +208,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
- private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
- private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,16 +302,31 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
};
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateAcessibilityStateFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ // TODO(b/198002034): Content observers currently can still be called back after
+ // being unregistered, and in this case we can ignore the change if the nav bar
+ // has been destroyed already
+ if (mNavigationBarView == null) {
+ return;
+ }
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
+ updateAssistantEntrypoints(available);
+ }
+ };
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
-
- // Send the assistant availability upon connection
- if (isConnected) {
- updateAssistantEntrypoints();
- }
}
@Override
@@ -421,20 +429,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
};
- private final ContentObserver mAssistContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // TODO(b/198002034): Content observers currently can still be called back after being
- // unregistered, and in this case we can ignore the change if the nav bar has been
- // destroyed already
- if (mNavigationBarView == null) {
- return;
- }
- updateAssistantEntrypoints();
- }
- };
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -504,7 +498,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -535,8 +529,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
+ mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -568,18 +561,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mCommandQueue.addCallback(this);
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mContentResolver = mContext.getContentResolver();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
- false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
+ mNavBarHelper.init(mContext);
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
@@ -593,7 +577,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
)).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -620,8 +603,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -643,7 +626,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -716,7 +699,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -885,7 +868,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
- pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
@@ -1175,7 +1157,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1400,8 +1382,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return true;
}
- void updateAccessibilityServicesState() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ void updateAcessibilityStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
if (mNavigationBarView != null) {
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
@@ -1413,7 +1395,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ a11yFlags = mNavBarHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1440,24 +1422,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
- private void updateAssistantEntrypoints() {
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
- boolean gestureDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ private void updateAssistantEntrypoints(boolean assistantAvailable) {
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
- && mAssistantTouchGestureEnabled
- && QuickStepContract.isGesturalMode(mNavBarMode));
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
@@ -1528,8 +1496,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- // update assistant entry points on system navigation radio button click
- updateAssistantEntrypoints();
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
@@ -1568,9 +1534,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
- this::updateAccessibilityServicesState;
-
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -1674,7 +1637,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
}
}
};
@@ -1710,7 +1673,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final NavBarHelper mNavBarHelper;
private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
@@ -1743,7 +1706,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -1773,7 +1736,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -1794,7 +1757,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
+ mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
deleted file mode 100644
index 13e6d8b410d6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.android.systemui.navigationbar;
-
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Extracts shared elements of a11y necessary between navbar and taskbar delegate
- */
-@SysUISingleton
-public final class NavigationBarA11yHelper implements
- AccessibilityButtonModeObserver.ModeChangedListener {
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
-
- @Inject
- public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
- mAccessibilityManager = accessibilityManager;
- accessibilityManagerWrapper.addCallback(
- accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
-
- mAccessibilityButtonModeObserver.addListener(this);
- }
-
- public void registerA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.add(listener);
- }
-
- public void removeA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.remove(listener);
- }
-
- private void dispatchEventUpdate() {
- for (NavA11yEventListener listener : mA11yEventListeners) {
- listener.updateAccessibilityServicesState();
- }
- }
-
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- dispatchEventUpdate();
- }
-
- /**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
- *
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
- */
- public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
- public interface NavA11yEventListener {
- void updateAccessibilityServicesState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3dc79c43d894..0429c022234d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
@@ -100,11 +101,12 @@ public class NavigationBarController implements
CommandQueue commandQueue,
@Main Handler mainHandler,
ConfigurationController configurationController,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController) {
mContext = context;
mHandler = mainHandler;
mNavigationBarFactory = navigationBarFactory;
@@ -115,8 +117,8 @@ public class NavigationBarController implements
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController);
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager, autoHideController, lightBarController);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index d707dbdf5cba..3d58a5a81251 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -43,6 +43,8 @@ import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
@@ -62,6 +64,9 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -73,19 +78,32 @@ import javax.inject.Singleton;
public class TaskbarDelegate implements CommandQueue.Callbacks,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
ComponentCallbacks, Dumpable {
+ private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
-
+ private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
- private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
private AutoHideController mAutoHideController;
+ private LightBarController mLightBarController;
+ private LightBarTransitionsController mLightBarTransitionsController;
private int mDisplayId;
private int mNavigationIconHints;
- private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
- this::updateSysuiFlags;
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateSysuiFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ updateAssistantAvailability(available);
+ }
+ };
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
@@ -125,27 +143,56 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
public void setDependencies(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ LightBarController lightBarController) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
dumpManager.registerDumpable(this);
mAutoHideController = autoHideController;
+ mLightBarController = lightBarController;
+ mLightBarTransitionsController = createLightBarTransitionsController();
+ }
+
+ // Separated into a method to keep setDependencies() clean/readable.
+ private LightBarTransitionsController createLightBarTransitionsController() {
+ return new LightBarTransitionsController(mContext,
+ new LightBarTransitionsController.DarkIntensityApplier() {
+ @Override
+ public void applyDarkIntensity(float darkIntensity) {
+ mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+ }
+
+ @Override
+ public int getTintAnimationDuration() {
+ return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+ }
+ }, mCommandQueue) {
+ @Override
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // Always tint taskbar nav buttons (region sampling handles gesture bar separately).
+ return true;
+ }
+ };
}
public void init(int displayId) {
+ if (mInitialized) {
+ return;
+ }
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
mNavigationModeController.addListener(this));
- mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.init(mContext);
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -154,23 +201,32 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
// Set initial state for any listeners
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
+ mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mInitialized = true;
}
public void destroy() {
+ if (!mInitialized) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
+ mLightBarTransitionsController.destroy(mContext);
+ mLightBarController.setNavigationBar(null);
+ mInitialized = false;
}
private void updateSysuiFlags() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -194,6 +250,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.commitUpdate(mDisplayId);
}
+ private void updateAssistantAvailability(boolean assistantAvailable) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
@@ -233,6 +301,10 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
InsetsVisibilities requestedVisibilities, String packageName) {
mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
+ if (mLightBarController != null && displayId == mDisplayId) {
+ mLightBarController.onNavigationBarAppearanceChanged(appearance, false/*nbModeChanged*/,
+ BarTransitions.MODE_TRANSPARENT /*navigationBarMode*/, navbarColorManagedByIme);
+ }
if (mBehavior != behavior) {
mBehavior = behavior;
updateSysuiFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 98b914672112..fce0c0c95e57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -17,8 +17,10 @@
package com.android.systemui.qs
import android.content.Intent
+import android.os.Handler
import android.os.UserManager
import android.provider.Settings
+import android.provider.Settings.Global.USER_SWITCHER_ENABLED
import android.view.View
import android.widget.Toast
import androidx.annotation.VisibleForTesting
@@ -35,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.phone.SettingsButton
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -42,6 +45,7 @@ import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.ViewController
+import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import javax.inject.Named
@@ -55,6 +59,7 @@ class FooterActionsController @Inject constructor(
private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val multiUserSwitchController: MultiUserSwitchController,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -64,7 +69,9 @@ class FooterActionsController @Inject constructor(
private val globalActionsDialog: GlobalActionsDialogLite,
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
- private val buttonsVisibleState: ExpansionState
+ private val buttonsVisibleState: ExpansionState,
+ private val globalSetting: GlobalSettings,
+ private val handler: Handler
) : ViewController<FooterActionsView>(view) {
enum class ExpansionState { COLLAPSED, EXPANDED }
@@ -83,6 +90,16 @@ class FooterActionsController @Inject constructor(
mView.onUserInfoChanged(picture, isGuestUser)
}
+ private val multiUserSetting =
+ object : SettingObserver(
+ globalSetting, handler, USER_SWITCHER_ENABLED, userTracker.userId) {
+ override fun handleValueChanged(value: Int, observedChange: Boolean) {
+ if (observedChange) {
+ updateView()
+ }
+ }
+ }
+
private val onClickListener = View.OnClickListener { v ->
// Don't do anything until views are unhidden. Don't do anything if the tap looks
// suspicious.
@@ -182,6 +199,7 @@ class FooterActionsController @Inject constructor(
return
}
this.listening = listening
+ multiUserSetting.isListening = listening
if (this.listening) {
userInfoController.addCallback(onUserInfoChangedListener)
updateView()
@@ -215,4 +233,4 @@ class FooterActionsController @Inject constructor(
}
private fun isTunerEnabled() = tunerService.isTunerEnabled
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index f6c89a9c66a2..dd4dc87d8a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -16,18 +16,22 @@
package com.android.systemui.qs
+import android.os.Handler
import android.os.UserManager
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.FooterActionsController.ExpansionState
import com.android.systemui.qs.dagger.QSFlagsModule
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import javax.inject.Named
@@ -35,6 +39,7 @@ class FooterActionsControllerBuilder @Inject constructor(
private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val deviceProvisionedController: DeviceProvisionedController,
@@ -43,7 +48,9 @@ class FooterActionsControllerBuilder @Inject constructor(
private val tunerService: TunerService,
private val globalActionsDialog: GlobalActionsDialogLite,
private val uiEventLogger: UiEventLogger,
- @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean
+ @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
+ private val globalSettings: GlobalSettings,
+ @Main private val handler: Handler
) {
private lateinit var view: FooterActionsView
private lateinit var buttonsVisibleState: ExpansionState
@@ -60,8 +67,9 @@ class FooterActionsControllerBuilder @Inject constructor(
fun build(): FooterActionsController {
return FooterActionsController(view, qsPanelController, activityStarter, userManager,
- userInfoController, multiUserSwitchControllerFactory.create(view),
+ userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
deviceProvisionedController, falsingManager, metricsLogger, tunerService,
- globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState)
+ globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
+ globalSettings, handler)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 71eb4a2e6cbb..d69deefc3477 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -103,6 +104,8 @@ public class QSPanel extends LinearLayout implements Tunable {
protected LinearLayout mHorizontalContentContainer;
protected QSTileLayout mTileLayout;
+ private float mSquishinessFraction = 1f;
+ private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -179,10 +182,26 @@ public class QSPanel extends LinearLayout implements Tunable {
if (mTileLayout == null) {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
.inflate(R.layout.qs_paged_tile_layout, this, false);
+ mTileLayout.setSquishinessFraction(mSquishinessFraction);
}
return mTileLayout;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ if (Float.compare(squishinessFraction, mSquishinessFraction) == 0) {
+ return;
+ }
+ mSquishinessFraction = squishinessFraction;
+ if (mTileLayout == null) {
+ return;
+ }
+ mTileLayout.setSquishinessFraction(squishinessFraction);
+ if (getMeasuredWidth() == 0) {
+ return;
+ }
+ updateViewPositions();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mTileLayout instanceof PagedTileLayout) {
@@ -228,6 +247,39 @@ public class QSPanel extends LinearLayout implements Tunable {
setMeasuredDimension(getMeasuredWidth(), height);
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ mChildrenLayoutTop.put(child, child.getTop());
+ }
+ updateViewPositions();
+ }
+
+ private void updateViewPositions() {
+ if (!(mTileLayout instanceof TileLayout)) {
+ return;
+ }
+ TileLayout layout = (TileLayout) mTileLayout;
+
+ // Adjust view positions based on tile squishing
+ int tileHeightOffset = layout.getTilesHeight() - layout.getHeight();
+
+ boolean move = false;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (move) {
+ int top = mChildrenLayoutTop.get(child);
+ child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
+ child.getRight(), top + tileHeightOffset + child.getHeight());
+ }
+ if (child == mTileLayout) {
+ move = true;
+ }
+ }
+ }
+
protected String getDumpableTag() {
return TAG;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index f7d1b1e2f5eb..eddc206db231 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -147,6 +147,10 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
return mMediaHost;
}
+ public void setSquishinessFraction(float squishinessFraction) {
+ mView.setSquishinessFraction(squishinessFraction);
+ }
+
@Override
protected void onViewAttached() {
mQsTileRevealController = createTileRevealController();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
index c1c146d40e38..c680cb5d18a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -1,14 +1,10 @@
package com.android.systemui.qs
-import android.view.ViewGroup
-import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
import com.android.systemui.qs.dagger.QSScope
import javax.inject.Inject
-import javax.inject.Named
@QSScope
class QSSquishinessController @Inject constructor(
- @Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
private val qsAnimator: QSAnimator,
private val qsPanelController: QSPanelController,
private val quickQSPanelController: QuickQSPanelController
@@ -33,23 +29,7 @@ class QSSquishinessController @Inject constructor(
* Change the height of all tiles and repositions their siblings.
*/
private fun updateSquishiness() {
- (qsPanelController.tileLayout as QSPanel.QSTileLayout).setSquishinessFraction(squishiness)
- val tileLayout = quickQSPanelController.tileLayout as TileLayout
- tileLayout.setSquishinessFraction(squishiness)
-
- // Calculate how much we should move the footer
- val tileHeightOffset = tileLayout.height - tileLayout.tilesHeight
- val footerTopMargin = (qqsFooterActionsView.layoutParams as ViewGroup.MarginLayoutParams)
- .topMargin
- val nextTop = tileLayout.bottom - tileHeightOffset + footerTopMargin
- val amountMoved = nextTop - qqsFooterActionsView.top
-
- // Move the footer and other siblings (MediaPlayer)
- (qqsFooterActionsView.parent as ViewGroup?)?.let { parent ->
- val index = parent.indexOfChild(qqsFooterActionsView)
- for (i in index until parent.childCount) {
- parent.getChildAt(i).top += amountMoved
- }
- }
+ qsPanelController.setSquishinessFraction(squishiness)
+ quickQSPanelController.setSquishinessFraction(squishiness)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 4bdeb567074e..09fad30f02ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -247,7 +247,10 @@ open class QSTileViewImpl @JvmOverloads constructor(
} else {
measuredHeight
}
- bottom = top + (actualHeight * squishinessFraction).toInt()
+ // Limit how much we affect the height, so we don't have rounding artifacts when the tile
+ // is too short.
+ val constrainedSquishiness = 0.1f + squishinessFraction * 0.9f
+ bottom = top + (actualHeight * constrainedSquishiness).toInt()
scrollY = (actualHeight - height) / 2
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 99cb700a324d..18b401f043d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -41,7 +42,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.Switch;
import android.widget.Toast;
@@ -53,6 +53,7 @@ import com.android.settingslib.notification.EnableZenModeDialog;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -84,6 +85,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
private final DndDetailAdapter mDetailAdapter;
private final SharedPreferences mSharedPreferences;
private final SettingObserver mSettingZenDuration;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private boolean mListening;
private boolean mShowingDetail;
@@ -100,7 +102,8 @@ public class DndTile extends QSTileImpl<BooleanState> {
QSLogger qsLogger,
ZenModeController zenModeController,
@Main SharedPreferences sharedPreferences,
- SecureSettings secureSettings
+ SecureSettings secureSettings,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -108,6 +111,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mController.observe(getLifecycle(), mZenCallback);
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler,
Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
@Override
@@ -117,8 +121,6 @@ public class DndTile extends QSTileImpl<BooleanState> {
};
}
-
-
public static void setVisible(Context context, boolean visible) {
Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
}
@@ -187,14 +189,17 @@ public class DndTile extends QSTileImpl<BooleanState> {
switch (zenDuration) {
case Settings.Secure.ZEN_DURATION_PROMPT:
mUiHandler.post(() -> {
- Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
- mDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- SystemUIDialog.setShowForAllUsers(mDialog, true);
- SystemUIDialog.registerDismissListener(mDialog);
- SystemUIDialog.setWindowOnTop(mDialog);
- mUiHandler.post(() -> mDialog.show());
- mHost.collapsePanels();
+ Dialog dialog = makeZenModeDialog();
+ if (view != null) {
+ final Dialog hostDialog =
+ mDialogLaunchAnimator.showFromView(dialog, view, false);
+ setDialogListeners(dialog, hostDialog);
+ } else {
+ // If we are not launching with animator, register default
+ // dismiss listener
+ SystemUIDialog.registerDismissListener(dialog);
+ dialog.show();
+ }
});
break;
case Settings.Secure.ZEN_DURATION_FOREVER:
@@ -209,6 +214,20 @@ public class DndTile extends QSTileImpl<BooleanState> {
}
}
+ private Dialog makeZenModeDialog() {
+ AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog)
+ .createDialog();
+ SystemUIDialog.applyFlags(dialog);
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ return dialog;
+ }
+
+ private void setDialogListeners(Dialog zenModeDialog, Dialog hostDialog) {
+ // Zen mode dialog is never hidden.
+ SystemUIDialog.registerDismissListener(zenModeDialog, hostDialog::dismiss);
+ zenModeDialog.setOnCancelListener(dialog -> hostDialog.cancel());
+ }
+
@Override
protected void handleSecondaryClick(@Nullable View view) {
if (mController.isVolumeRestricted()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 77b9cc14fa6d..883552a1f7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -56,6 +56,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -120,6 +121,7 @@ public class InternetDialog extends SystemUIDialog implements
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
private Switch mMobileDataToggle;
+ private View mMobileToggleDivider;
private Switch mWiFiToggle;
private FrameLayout mDoneLayout;
private Drawable mBackgroundOn;
@@ -207,6 +209,7 @@ public class InternetDialog extends SystemUIDialog implements
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
@@ -378,6 +381,14 @@ public class InternetDialog extends SystemUIDialog implements
mMobileNetworkLayout.setBackground(
isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
+ TypedArray array = mContext.obtainStyledAttributes(
+ R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
+ int dividerColor = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.textColorSecondary);
+ mMobileToggleDivider.setBackgroundColor(isCarrierNetworkConnected
+ ? array.getColor(0, dividerColor) : dividerColor);
+ array.recycle();
+
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fa874b19c2cc..3ed7e84af020 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -986,6 +986,18 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
+ public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
+ }
+ }
+
private void updateEnabledState() {
final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId();
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 75b3592d4ac6..3cecbb71407a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -533,9 +533,13 @@ public class CommandQueue extends IStatusBar.Stub implements
* @param animate {@code true} to show animations.
*/
public void recomputeDisableFlags(int displayId, boolean animate) {
- int disabled1 = getDisabled1(displayId);
- int disabled2 = getDisabled2(displayId);
- disable(displayId, disabled1, disabled2, animate);
+ // This must update holding the lock otherwise it can clobber the disabled flags set on the
+ // binder thread from the disable() call
+ synchronized (mLock) {
+ int disabled1 = getDisabled1(displayId);
+ int disabled2 = getDisabled2(displayId);
+ disable(displayId, disabled1, disabled2, animate);
+ }
}
private void setDisabled(int displayId, int disabled1, int disabled2) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e78b4f43f00a..0389a7b01c72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -791,6 +791,7 @@ public class NotificationEntryManager implements
* these don't exist, although there are a couple exceptions.
*/
public Iterable<NotificationEntry> getPendingNotificationsIterator() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mPendingNotifications.values();
}
@@ -803,6 +804,7 @@ public class NotificationEntryManager implements
* @return a {@link NotificationEntry} if it has been prepared, else null
*/
public NotificationEntry getActiveNotificationUnfiltered(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mActiveNotifications.get(key);
}
@@ -811,6 +813,7 @@ public class NotificationEntryManager implements
* notification doesn't exist.
*/
public NotificationEntry getPendingOrActiveNotif(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
NotificationEntry entry = mPendingNotifications.get(key);
if (entry != null) {
return entry;
@@ -945,6 +948,7 @@ public class NotificationEntryManager implements
* @return A read-only list of the currently active notifications
*/
public List<NotificationEntry> getVisibleNotifications() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications;
}
@@ -954,17 +958,20 @@ public class NotificationEntryManager implements
*/
@Override
public Collection<NotificationEntry> getAllNotifs() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyAllNotifications;
}
@Nullable
@Override
public NotificationEntry getEntry(String key) {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return getPendingOrActiveNotif(key);
}
/** @return A count of the active notifications */
public int getActiveNotificationsCount() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications.size();
}
@@ -972,6 +979,7 @@ public class NotificationEntryManager implements
* @return {@code true} if there is at least one notification that should be visible right now
*/
public boolean hasActiveNotifications() {
+ mNotifPipelineFlags.checkLegacyPipelineEnabled();
return mReadOnlyNotifications.size() != 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
index 992d898531b8..bd011c3847ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import androidx.annotation.NonNull;
import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -26,6 +27,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
/**
@@ -34,14 +37,17 @@ import javax.inject.Inject;
*/
@CoordinatorScope
public class CommunalCoordinator implements Coordinator {
+ final Executor mExecutor;
final CommunalStateController mCommunalStateController;
final NotificationEntryManager mNotificationEntryManager;
final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Inject
- public CommunalCoordinator(NotificationEntryManager notificationEntryManager,
+ public CommunalCoordinator(@Main Executor executor,
+ NotificationEntryManager notificationEntryManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
CommunalStateController communalStateController) {
+ mExecutor = executor;
mNotificationEntryManager = notificationEntryManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
mCommunalStateController = communalStateController;
@@ -57,8 +63,10 @@ public class CommunalCoordinator implements Coordinator {
final CommunalStateController.Callback mStateCallback = new CommunalStateController.Callback() {
@Override
public void onCommunalViewShowingChanged() {
- mFilter.invalidateList();
- mNotificationEntryManager.updateNotifications("Communal mode state changed");
+ mExecutor.execute(() -> {
+ mFilter.invalidateList();
+ mNotificationEntryManager.updateNotifications("Communal mode state changed");
+ });
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 9411de75ba7f..999ef9c99dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -47,7 +47,7 @@ import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.*
+import java.util.Optional
import javax.inject.Inject
/**
@@ -152,8 +152,14 @@ class NotificationsControllerImpl @Inject constructor(
}
override fun resetUserExpandedStates() {
- for (entry in entryManager.visibleNotifications) {
- entry.resetUserExpansion()
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ for (entry in notifPipeline.get().allNotifs) {
+ entry.resetUserExpansion()
+ }
+ } else {
+ for (entry in entryManager.visibleNotifications) {
+ entry.resetUserExpansion()
+ }
}
}
@@ -167,9 +173,12 @@ class NotificationsControllerImpl @Inject constructor(
}
}
- override fun getActiveNotificationsCount(): Int {
- return entryManager.activeNotificationsCount
- }
+ override fun getActiveNotificationsCount(): Int =
+ if (notifPipelineFlags.isNewPipelineEnabled()) {
+ notifPipeline.get().getShadeListCount()
+ } else {
+ entryManager.activeNotificationsCount
+ }
companion object {
// NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index eb7410c08733..1038e76234ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -468,8 +468,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
final int height = (view instanceof ExpandableView)
? ((ExpandableView) view).getActualHeight()
: view.getHeight();
- final int rx = (int) ev.getX();
- final int ry = (int) ev.getY();
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
int[] temp = new int[2];
view.getLocationOnScreen(temp);
final int x = temp[0];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 67f51cb9ab43..aa3b3e12b8f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
@@ -46,9 +47,11 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -71,6 +74,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
+ private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -167,6 +171,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
+ private final StatusBarStateController mStatusBarStateController;
+
+ private long mLastFpFailureUptimeMillis;
+ private int mNumConsecutiveFpFailures;
private static final class PendingAuthenticated {
public final int userId;
@@ -209,7 +217,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
BIOMETRIC_IRIS_FAILURE(403),
@UiEvent(doc = "A biometric event of type iris errored.")
- BIOMETRIC_IRIS_ERROR(404);
+ BIOMETRIC_IRIS_ERROR(404),
+
+ @UiEvent(doc = "Bouncer was shown as a result of consecutive failed UDFPS attempts.")
+ BIOMETRIC_BOUNCER_SHOWN(916);
private final int mId;
@@ -257,7 +268,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
NotificationMediaManager notificationMediaManager,
WakefulnessLifecycle wakefulnessLifecycle,
ScreenLifecycle screenLifecycle,
- AuthController authController) {
+ AuthController authController,
+ StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -279,6 +291,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
mAuthController = authController;
+ mStatusBarStateController = statusBarStateController;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -620,6 +633,22 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
.setType(MetricsEvent.TYPE_FAILURE).setSubtype(toSubtype(biometricSourceType)));
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ long currUptimeMillis = SystemClock.uptimeMillis();
+ if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
+ mNumConsecutiveFpFailures += 1;
+ } else {
+ mNumConsecutiveFpFailures = 1;
+ }
+ mLastFpFailureUptimeMillis = currUptimeMillis;
+
+ if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+ mKeyguardViewController.showBouncer(true);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+ mNumConsecutiveFpFailures = 0;
+ }
cleanup();
}
@@ -631,6 +660,16 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
.addTaggedData(MetricsEvent.FIELD_BIOMETRIC_AUTH_ERROR, msgId));
Optional.ofNullable(BiometricUiEvent.ERROR_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(UI_EVENT_LOGGER::log);
+
+ // if we're on the shade and we're locked out, immediately show the bouncer
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ && (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && mUpdateMonitor.isUdfpsSupported()
+ && (mStatusBarStateController.getState() == StatusBarState.SHADE
+ || mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
+ mKeyguardViewController.showBouncer(true);
+ }
cleanup();
}
@@ -664,6 +703,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mBiometricModeListener.onResetMode();
mBiometricModeListener.notifyBiometricAuthModeChanged();
}
+ mNumConsecutiveFpFailures = 0;
+ mLastFpFailureUptimeMillis = 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 353868ba969f..9647486be992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -21,6 +21,7 @@ import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,6 +82,13 @@ public class KeyguardBouncer {
public void onStrongAuthStateChanged(int userId) {
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
+
+ @Override
+ public void onLockedOutStateChanged(BiometricSourceType type) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ mBouncerPromptReason = mCallback.getBouncerPromptReason();
+ }
+ }
};
private final Runnable mRemoveViewRunnable = this::removeView;
private final KeyguardBypassController mKeyguardBypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 570b0ca3564c..88ae0db5bad0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -37,7 +37,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.policy.BatteryController;
import java.io.FileDescriptor;
@@ -251,7 +250,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private void updateNavigation() {
if (mNavigationBarController != null
- && !QuickStepContract.isGesturalMode(mNavigationMode)) {
+ && mNavigationBarController.supportsIconTintForNavMode(mNavigationMode)) {
mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 9021b74d1518..415fb92e37ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -28,6 +28,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -230,6 +231,14 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
}
/**
+ * Return whether to use the tint calculated in this class for nav icons.
+ */
+ public boolean supportsIconTintForNavMode(int navigationMode) {
+ // In gesture mode, we already do region sampling to update tint based on content beneath.
+ return !QuickStepContract.isGesturalMode(navigationMode);
+ }
+
+ /**
* Interface to apply a specific dark intensity.
*/
public interface DarkIntensityApplier {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a9753acc2de0..16aac4d765a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -506,23 +506,11 @@ public class NotificationPanelViewController extends PanelViewController {
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
- private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback =
- new CommunalSourceMonitor.Callback() {
- @Override
- public void onSourceAvailable(WeakReference<CommunalSource> source) {
- setCommunalSource(source);
- }
- };
+ private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback;
private WeakReference<CommunalSource> mCommunalSource;
- private final CommunalSource.Callback mCommunalSourceCallback =
- new CommunalSource.Callback() {
- @Override
- public void onDisconnected() {
- setCommunalSource(null /*source*/);
- }
- };
+ private final CommunalSource.Callback mCommunalSourceCallback;
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -904,6 +892,15 @@ public class NotificationPanelViewController extends PanelViewController {
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
+
+ mCommunalSourceCallback = () -> {
+ mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
+ };
+
+ mCommunalSourceMonitorCallback = (source) -> {
+ mUiExecutor.execute(() -> setCommunalSource(source));
+ };
+
updateUserSwitcherFlags();
onFinishInflate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index a77a097f0453..ae3b7ee1c809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -30,11 +30,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.media.AudioAttributes;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
@@ -106,10 +106,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@Inject
StatusBarCommandQueueCallbacks(
@@ -611,9 +609,9 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
}
private void vibrateForCameraGesture() {
- // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
mVibratorOptional.ifPresent(
- v -> v.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES));
+ v -> v.vibrate(mCameraLaunchGestureVibrationEffect,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES));
}
private static VibrationEffect getCameraGestureVibrationEffect(
@@ -627,6 +625,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
.compose();
}
if (vibratorOptional.isPresent() && vibratorOptional.get().hasAmplitudeControl()) {
+ // Make sure to pass -1 for repeat so VibratorManagerService doesn't stop us when going
+ // to sleep.
return VibrationEffect.createWaveform(
StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7ab4a1ec237e..0d23d663c51c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -441,6 +441,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showBouncer(boolean scrimmed) {
+ resetAlternateAuth(false);
+
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 1130ec24108a..ed52a81751dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -36,6 +36,8 @@ import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import androidx.annotation.Nullable;
+
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.DialogListener;
@@ -303,13 +305,32 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog,
* the screen off / close system dialogs broadcast.
* <p>
* <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
- * calling this because it causes a leak of BroadcastReceiver.
+ * calling this because it causes a leak of BroadcastReceiver. Instead, call the version that
+ * takes an extra Runnable as a parameter.
*
* @param dialog The dialog to be associated with the listener.
*/
public static void registerDismissListener(Dialog dialog) {
+ registerDismissListener(dialog, null);
+ }
+
+
+ /**
+ * Registers a listener that dismisses the given dialog when it receives
+ * the screen off / close system dialogs broadcast.
+ * <p>
+ * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
+ * calling this because it causes a leak of BroadcastReceiver.
+ *
+ * @param dialog The dialog to be associated with the listener.
+ * @param dismissAction An action to run when the dialog is dismissed.
+ */
+ public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) {
DismissReceiver dismissReceiver = new DismissReceiver(dialog);
- dialog.setOnDismissListener(d -> dismissReceiver.unregister());
+ dialog.setOnDismissListener(d -> {
+ dismissReceiver.unregister();
+ if (dismissAction != null) dismissAction.run();
+ });
dismissReceiver.register();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
index 58e0cb259bb2..3696ec540baf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
@@ -16,22 +16,53 @@
package com.android.systemui.animation
+import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.widget.LinearLayout
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewParent
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class GhostedViewLaunchAnimatorControllerTest : SysuiTestCase() {
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var view: View
+ @Mock lateinit var rootView: ViewGroup
+ @Mock lateinit var viewParent: ViewParent
+ @Mock lateinit var drawable: Drawable
+ lateinit var controller: GhostedViewLaunchAnimatorController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(view.rootView).thenReturn(rootView)
+ whenever(view.background).thenReturn(drawable)
+ whenever(view.height).thenReturn(0)
+ whenever(view.width).thenReturn(0)
+ whenever(view.parent).thenReturn(viewParent)
+ whenever(view.visibility).thenReturn(View.VISIBLE)
+ whenever(view.invalidate()).then { /* NO-OP */ }
+ whenever(view.getLocationOnScreen(any())).then { /* NO-OP */ }
+ whenever(interactionJankMonitor.begin(any(), anyInt())).thenReturn(true)
+ whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
+ controller = GhostedViewLaunchAnimatorController(view, 0, interactionJankMonitor)
+ }
+
@Test
fun animatingOrphanViewDoesNotCrash() {
- val ghostedView = LinearLayout(mContext)
- val controller = GhostedViewLaunchAnimatorController(ghostedView)
val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
controller.onIntentStarted(willAnimate = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0e86964147d7..1cf21ac40e31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -351,9 +351,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mSystemClock.advanceTime(205);
mController.onTouchOutsideView();
- // THEN show the bouncer and reset alt auth
+ // THEN show the bouncer
verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
- verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
new file mode 100644
index 000000000000..659b1a31a7a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourcePrimerTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSourcePrimerTest extends SysuiTestCase {
+ private static final String TEST_COMPONENT_NAME = "com.google.tests/.CommunalService";
+ private static final int MAX_RETRIES = 5;
+ private static final int RETRY_DELAY_MS = 1000;
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private Resources mResources;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Mock
+ private CommunalSource mSource;
+
+ @Mock
+ private CommunalSourceMonitor mCommunalSourceMonitor;
+
+ @Mock
+ private CommunalSource.Connector mConnector;
+
+ @Mock
+ private CommunalSource.Observer mObserver;
+
+ private CommunalSourcePrimer mPrimer;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mResources.getInteger(R.integer.config_communalSourceMaxReconnectAttempts))
+ .thenReturn(MAX_RETRIES);
+ when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
+ .thenReturn(RETRY_DELAY_MS);
+ when(mResources.getString(R.string.config_communalSourceComponent))
+ .thenReturn(TEST_COMPONENT_NAME);
+
+ mPrimer = new CommunalSourcePrimer(mContext, mResources, mFakeExecutor,
+ mCommunalSourceMonitor, Optional.of(mConnector), Optional.of(mObserver));
+ }
+
+ @Test
+ public void testConnect() {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.of(mSource));
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSourceMonitor).setSource(mSource);
+ }
+
+ @Test
+ public void testRetryOnBindFailure() throws Exception {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.empty());
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mConnector, times(1)).connect();
+ clearInvocations(mConnector);
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+
+ verify(mCommunalSourceMonitor, never()).setSource(Mockito.notNull());
+ }
+
+ @Test
+ public void testAttemptOnPackageChange() {
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.empty());
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ final ArgumentCaptor<CommunalSource.Observer.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(CommunalSource.Observer.Callback.class);
+ verify(mObserver).addCallback(callbackCaptor.capture());
+
+ clearInvocations(mConnector);
+ callbackCaptor.getValue().onSourceChanged();
+
+ verify(mConnector, times(1)).connect();
+ }
+
+ @Test
+ public void testDisconnect() {
+ final ArgumentCaptor<CommunalSource.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(CommunalSource.Callback.class);
+
+ when(mConnector.connect()).thenReturn(
+ CallbackToFutureAdapter.getFuture(completer -> {
+ completer.set(Optional.of(mSource));
+ return "test";
+ }));
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+ verify(mCommunalSourceMonitor).setSource(mSource);
+ verify(mSource).addCallback(callbackCaptor.capture());
+
+ clearInvocations(mConnector);
+ callbackCaptor.getValue().onDisconnected();
+ mFakeExecutor.runAllReady();
+
+ verify(mConnector).connect();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6d8645e44fb0..b774daf157c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -41,6 +41,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardDisplayManager;
@@ -64,9 +65,6 @@ import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
-import java.util.Optional;
-import java.util.function.Function;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +73,9 @@ import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+import java.util.function.Function;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -103,6 +104,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
+ private @Mock InteractionJankMonitor mInteractionJankMonitor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -121,6 +123,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
.thenReturn(mUnfoldAnimationOptional);
when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
+ when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
+ when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
mViewMediator = new KeyguardViewMediator(
mContext,
@@ -144,7 +148,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mKeyguardStateController,
() -> mKeyguardUnlockAnimationController,
mUnlockedScreenOffAnimationController,
- () -> mNotificationShadeDepthController);
+ () -> mNotificationShadeDepthController,
+ mInteractionJankMonitor);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 175ec87fd943..a6e567ea8b5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -104,37 +104,54 @@ class MediaCarouselControllerTest : SysuiTestCase() {
fun testPlayerOrdering() {
// Test values: key, data, last active time
val playingLocal = Triple("playing local",
- DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
4500L)
- val playingRemote = Triple("playing remote",
- DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
+ val playingCast = Triple("playing cast",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
5000L)
val pausedLocal = Triple("paused local",
- DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
1000L)
- val pausedRemote = Triple("paused remote",
- DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
+ val pausedCast = Triple("paused cast",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
2000L)
+ val playingRcn = Triple("playing RCN",
+ DATA.copy(active = true, isPlaying = true,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
+ val pausedRcn = Triple("paused RCN",
+ DATA.copy(active = true, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
+ 5000L)
+
val resume1 = Triple("resume 1",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
500L)
val resume2 = Triple("resume 2",
- DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
1000L)
// Expected ordering for media players:
// Actively playing local sessions
- // Actively playing remote sessions
- // Paused sessions, by last active
+ // Actively playing cast sessions
+ // Paused local and cast sessions, by last active
+ // RCNs
// Resume controls, by last active
- val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
- resume1)
+ val expected = listOf(playingLocal, playingCast, pausedCast, pausedLocal, playingRcn,
+ pausedRcn, resume2, resume1)
expected.forEach {
clock.setCurrentTimeMillis(it.third)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 66b64708ad24..f870da3e68d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -74,8 +74,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.addListener(mListener);
mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true,
- false, KEY, false, false, false, 0L);
+ new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null,
+ MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 2b2fc513d03c..f44cc38c78c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.Notification
import android.app.Notification.MediaStyle
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
@@ -229,6 +230,30 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
+ fun testOnNotificationAdded_isRcn_markedRemote() {
+ val bundle = Bundle().apply {
+ putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Remote Cast Notification")
+ }
+ val rcn = SbnBuilder().run {
+ setPkg("com.android.systemui") // System package
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.addExtras(bundle)
+ }
+ build()
+ }
+
+ mediaDataManager.onNotificationAdded(KEY, rcn)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
+ assertThat(mediaDataCaptor.value!!.playbackLocation).isEqualTo(
+ MediaData.PLAYBACK_CAST_REMOTE)
+ }
+
+ @Test
fun testOnNotificationRemoved_callsListener() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
@@ -306,7 +331,8 @@ class MediaDataManagerTest : SysuiTestCase() {
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false))
val data = mediaDataCaptor.value
- val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false)
+ val dataRemoteWithResume = data.copy(resumeAction = Runnable {},
+ playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume)
// WHEN the notification is removed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index 8dc9eff97ab9..421f9bee78fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -42,7 +42,8 @@ public class MediaPlayerDataTest : SysuiTestCase() {
val mockito = MockitoJUnit.rule()
companion object {
- val LOCAL = true
+ val LOCAL = MediaData.PLAYBACK_LOCAL
+ val REMOTE = MediaData.PLAYBACK_CAST_LOCAL
val RESUMPTION = true
val PLAYING = true
val UNDETERMINED = null
@@ -58,7 +59,7 @@ public class MediaPlayerDataTest : SysuiTestCase() {
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsRemote = mock(MediaControlPanel::class.java)
- val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote, systemClock)
MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying, systemClock)
@@ -100,13 +101,13 @@ public class MediaPlayerDataTest : SysuiTestCase() {
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
- val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, REMOTE, !RESUMPTION)
val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java)
val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION)
val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java)
- val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION)
+ val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, REMOTE, !RESUMPTION)
val playerCanResume = mock(MediaControlPanel::class.java)
val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION)
@@ -127,8 +128,8 @@ public class MediaPlayerDataTest : SysuiTestCase() {
val players = MediaPlayerData.players()
assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerCanResume,
- playerUndetermined).inOrder()
+ playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerUndetermined,
+ playerCanResume).inOrder()
}
@Test
@@ -160,9 +161,10 @@ public class MediaPlayerDataTest : SysuiTestCase() {
private fun createMediaData(
app: String,
isPlaying: Boolean?,
- isLocalSession: Boolean,
+ location: Int,
resumption: Boolean
) =
- MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "",
- null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying)
+ MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(),
+ "package:" + app, null, null, null, true, null, location, resumption, "key:" + app,
+ false, isPlaying)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index a17a03daba5b..30ee2e4d3431 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -211,10 +211,20 @@ class MediaResumeListenerTest : SysuiTestCase() {
}
@Test
- fun testOnLoad_remotePlayback_doesNotCheck() {
- // When media data is loaded that has not been checked yet, and is not local
- val dataRemote = data.copy(isLocalSession = false)
- resumeListener.onMediaDataLoaded(KEY, null, dataRemote)
+ fun testOnLoad_localCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a local cast
+ val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCast)
+
+ // Then we do not take action
+ verify(mediaDataManager, never()).setResumeAction(any(), any())
+ }
+
+ @Test
+ fun testOnload_remoteCast_doesNotCheck() {
+ // When media data is loaded that has not been checked yet, and is a remote cast
+ val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE)
+ resumeListener.onMediaDataLoaded(KEY, null, dataRcn)
// Then we do not take action
verify(mediaDataManager, never()).setResumeAction(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
new file mode 100644
index 000000000000..734faec4ec74
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavBarHelperTest extends SysuiTestCase {
+
+ @Mock
+ AccessibilityManager mAccessibilityManager;
+ @Mock
+ AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock
+ AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ @Mock
+ OverviewProxyService mOverviewProxyService;
+ @Mock
+ Lazy<AssistManager> mAssistManagerLazy;
+ @Mock
+ AssistManager mAssistManager;
+ @Mock
+ NavigationModeController mNavigationModeController;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ ComponentName mAssistantComponent;
+ @Mock
+ DumpManager mDumpManager;
+ @Mock
+ NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+
+ private NavBarHelper mNavBarHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
+ when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ mNavBarHelper = new NavBarHelper(mAccessibilityManager,
+ mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
+ mOverviewProxyService, mAssistManagerLazy, mNavigationModeController,
+ mUserTracker, mDumpManager);
+
+ }
+
+ @Test
+ public void registerListenersInCtor() {
+ verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
+ verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
+ verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ }
+
+ @Test
+ public void registerAssistantContentObserver() {
+ mNavBarHelper.init(mContext);
+ verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
+ }
+
+ @Test
+ public void callbacksFiredWhenRegistering() {
+ mNavBarHelper.init(mContext);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterConnecting() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onConnectionChanged(false);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+
+ mNavBarHelper.onConnectionChanged(true);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void a11yCallbacksFiredAfterModeChange() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterNavModeChange() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onNavigationModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void removeListenerNoCallbacksFired() {
+ mNavBarHelper.init(mContext);
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Remove listener
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Would have fired 2nd callback if not removed
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 4fc329ffc7af..9d2541c0150f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.After;
@@ -82,11 +83,12 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mCommandQueue,
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
- mock(NavigationBarA11yHelper.class),
+ mock(NavBarHelper.class),
mock(TaskbarDelegate.class),
mNavigationBarFactory,
mock(DumpManager.class),
- mock(AutoHideController.class)));
+ mock(AutoHideController.class),
+ mock(LightBarController.class)));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 50b717181dc2..e038b6e6dfb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -136,7 +136,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
@Mock
- NavigationBarA11yHelper mNavigationBarA11yHelper;
+ NavBarHelper mNavBarHelper;
@Mock
private LightBarController mLightBarController;
@Mock
@@ -227,6 +227,7 @@ public class NavigationBarTest extends SysuiTestCase {
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
.setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
.build());
+ when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
@@ -330,14 +331,14 @@ public class NavigationBarTest extends SysuiTestCase {
public void testA11yEventAfterDetach() {
View v = mNavigationBar.createView(null);
mNavigationBar.onViewAttachedToWindow(v);
- verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).registerNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
mNavigationBar.onViewDetachedFromWindow(v);
- verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).removeNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAccessibilityServicesState();
+ mNavigationBar.updateAcessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -367,7 +368,7 @@ public class NavigationBarTest extends SysuiTestCase {
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mNavigationBarA11yHelper,
+ mNavBarHelper,
mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index d2bba361b1f8..26f04fc4e7b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -1,6 +1,8 @@
package com.android.systemui.qs
+import android.os.Handler
import android.os.UserManager
+import android.provider.Settings
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -16,10 +18,12 @@ import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.FakeTunerService
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.google.common.truth.Truth.assertThat
@@ -42,6 +46,8 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Mock
private lateinit var userManager: UserManager
@Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var deviceProvisionedController: DeviceProvisionedController
@@ -62,11 +68,13 @@ class FooterActionsControllerTest : LeakCheckedTest() {
private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
+ private lateinit var fakeSettings: FakeSettings
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ fakeSettings = FakeSettings()
injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
@@ -74,10 +82,11 @@ class FooterActionsControllerTest : LeakCheckedTest() {
.inflate(R.layout.footer_actions, null) as FooterActionsView
controller = FooterActionsController(view, qsPanelController, activityStarter,
- userManager, userInfoController, multiUserSwitchController,
+ userManager, userTracker, userInfoController, multiUserSwitchController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true,
- buttonsVisibleState = ExpansionState.EXPANDED)
+ buttonsVisibleState = ExpansionState.EXPANDED, fakeSettings,
+ Handler(testableLooper.looper))
controller.init()
ViewUtils.attachView(view)
// View looper is the testable looper associated with the test
@@ -122,4 +131,24 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
}
+
+ @Test
+ fun testMultiUserSwitchUpdatedWhenSettingChanged() {
+ // When expanded, listening is true
+ controller.setListening(true)
+ testableLooper.processAllMessages()
+
+ val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
+ assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+
+ // The setting is only used as an indicator for whether the view should refresh. The actual
+ // value of the setting is ignored; isMultiUserEnabled is the source of truth
+ whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
+
+ // Changing the value of USER_SWITCHER_ENABLED should cause the view to update
+ fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
+ testableLooper.processAllMessages()
+
+ assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index f41d7b127a9e..e2a0626d9849 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -1,7 +1,6 @@
package com.android.systemui.qs
import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -9,7 +8,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -18,13 +16,9 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
class QSSquishinessControllerTest : SysuiTestCase() {
- @Mock private lateinit var qqsFooterActionsView: FooterActionsView
- @Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
@Mock private lateinit var qsAnimator: QSAnimator
@Mock private lateinit var qsPanelController: QSPanelController
@Mock private lateinit var quickQsPanelController: QuickQSPanelController
- @Mock private lateinit var tileLayout: TileLayout
- @Mock private lateinit var pagedTileLayout: PagedTileLayout
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -32,11 +26,8 @@ class QSSquishinessControllerTest : SysuiTestCase() {
@Before
fun setup() {
- qsSquishinessController = QSSquishinessController(qqsFooterActionsView, qsAnimator,
+ qsSquishinessController = QSSquishinessController(qsAnimator,
qsPanelController, quickQsPanelController)
- `when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
- `when`(qsPanelController.tileLayout).thenReturn(pagedTileLayout)
- `when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
}
@Test
@@ -51,7 +42,7 @@ class QSSquishinessControllerTest : SysuiTestCase() {
@Test
fun setSquishiness_updatesTiles() {
qsSquishinessController.squishiness = 0.5f
- verify(tileLayout).setSquishinessFraction(0.5f)
- verify(pagedTileLayout).setSquishinessFraction(0.5f)
+ verify(qsPanelController).setSquishinessFraction(0.5f)
+ verify(quickQsPanelController).setSquishinessFraction(0.5f)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index f99703e2415d..3ea2cc582ba3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -16,22 +16,28 @@
package com.android.systemui.qs.tiles
+import android.app.Dialog
import android.content.ContextWrapper
import android.content.SharedPreferences
import android.os.Handler
import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
@@ -40,9 +46,12 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.io.File
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -70,6 +79,10 @@ class DndTileTest : SysuiTestCase() {
private lateinit var zenModeController: ZenModeController
@Mock
private lateinit var sharedPreferences: SharedPreferences
+ @Mock
+ private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock
+ private lateinit var hostDialog: Dialog
private lateinit var secureSettings: SecureSettings
private lateinit var testableLooper: TestableLooper
@@ -81,15 +94,17 @@ class DndTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- Mockito.`when`(qsHost.userId).thenReturn(DEFAULT_USER)
- Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ whenever(qsHost.userId).thenReturn(DEFAULT_USER)
+ whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ whenever(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
+ .thenReturn(hostDialog)
val wrappedContext = object : ContextWrapper(context) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
return sharedPreferences
}
}
- Mockito.`when`(qsHost.context).thenReturn(wrappedContext)
+ whenever(qsHost.context).thenReturn(wrappedContext)
tile = DndTile(
qsHost,
@@ -102,7 +117,8 @@ class DndTileTest : SysuiTestCase() {
qsLogger,
zenModeController,
sharedPreferences,
- secureSettings
+ secureSettings,
+ dialogLaunchAnimator
)
}
@@ -147,4 +163,32 @@ class DndTileTest : SysuiTestCase() {
assertThat(tile.state.forceExpandIcon).isTrue()
}
+
+ @Test
+ fun testLaunchDialogFromViewWhenPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator).showFromView(any(), eq(view), anyBoolean())
+ }
+
+ @Test
+ fun testNoLaunchDialogWhenNotPrompt() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+
+ secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
+ testableLooper.processAllMessages()
+
+ val view = View(context)
+ tile.handleClick(view)
+ testableLooper.processAllMessages()
+
+ verify(dialogLaunchAnimator, never()).showFromView(any(), any(), anyBoolean())
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
index 01e4cce0cc30..f4452bc248b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.test.suitebuilder.annotation.SmallTest;
@@ -29,6 +30,8 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +42,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class CommunalCoordinatorTest extends SysuiTestCase {
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+
@Mock
CommunalStateController mCommunalStateController;
@Mock
@@ -57,7 +62,7 @@ public class CommunalCoordinatorTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mCoordinator = new CommunalCoordinator(mNotificationEntryManager,
+ mCoordinator = new CommunalCoordinator(mExecutor, mNotificationEntryManager,
mNotificationLockscreenUserManager, mCommunalStateController);
}
@@ -84,6 +89,12 @@ public class CommunalCoordinatorTest extends SysuiTestCase {
// Verify that notifications are filtered out when communal is showing and that the filter
// pipeline is notified.
stateCallback.onCommunalViewShowingChanged();
+ // Make sure callback depends on executor to run.
+ verify(mFilterListener, never()).onPluggableInvalidated(any());
+ verify(mNotificationEntryManager, never()).updateNotifications(any());
+
+ mExecutor.runAllReady();
+
verify(mFilterListener).onPluggableInvalidated(any());
verify(mNotificationEntryManager).updateNotifications(any());
assert (filter.shouldFilterOut(mNotificationEntry, 0));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 0faf5d478116..a0e91fc77148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -438,8 +438,8 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mView).getWidth();
doReturn(20).when(mView).getHeight();
@@ -455,7 +455,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mView));
@@ -466,8 +466,8 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
assertEquals("returns false when view is null", false,
NotificationSwipeHelper.isTouchInView(mEvent, null));
- doReturn(5f).when(mEvent).getX();
- doReturn(10f).when(mEvent).getY();
+ doReturn(5f).when(mEvent).getRawX();
+ doReturn(10f).when(mEvent).getRawY();
doReturn(20).when(mNotificationRow).getWidth();
doReturn(20).when(mNotificationRow).getActualHeight();
@@ -483,7 +483,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
assertTrue("Touch is within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
- doReturn(50f).when(mEvent).getX();
+ doReturn(50f).when(mEvent).getRawX();
assertFalse("Touch is not within the view",
mSwipeHelper.isTouchInView(mEvent, mNotificationRow));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 25fd80133897..07debe68e224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -101,6 +102,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -123,7 +126,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController);
+ mAuthController, mStatusBarStateController);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
@@ -378,6 +381,23 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
+ public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+ // GIVEN UDFPS is supported
+ when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+
+ // WHEN udfps fails twice - then don't show the bouncer
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+
+ // WHEN udfps fails the third time
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
+
+ @Test
public void onFinishedGoingToSleep_authenticatesWhenPending() {
when(mUpdateMonitor.isGoingToSleep()).thenReturn(true);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 81ddc670745b..270c64ddfa7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -358,6 +358,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
@@ -511,7 +512,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mQuickAccessWalletController,
mQrCodeScannerController,
mRecordingController,
- new FakeExecutor(new FakeSystemClock()),
+ mExecutor,
mSecureSettings,
mSplitShadeHeaderController,
mUnlockedScreenOffAnimationController,
@@ -936,6 +937,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(WeakReference.class);
monitorCallback.getValue().onSourceAvailable(new WeakReference<>(mCommunalSource));
+ mExecutor.runAllReady();
verify(mCommunalHostViewController).show(sourceCapture.capture());
assertThat(sourceCapture.getValue().get()).isEqualTo(mCommunalSource);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6f174cbe0021..c5bdfed6082b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -374,6 +374,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void testHideAltAuth_onShowBouncer() {
+ // GIVEN alt auth is showing
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()).thenReturn(true);
+ reset(mAlternateAuthInterceptor);
+
+ // WHEN showBouncer is called
+ mStatusBarKeyguardViewManager.showBouncer(true);
+
+ // THEN alt bouncer should be hidden
+ verify(mAlternateAuthInterceptor).hideAlternateAuthBouncer();
+ }
+
+ @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 67bb7262b672..f1599e4d7ffa 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -39,6 +39,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -1101,8 +1102,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
- return magnificationProcessor
- .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId);
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ return magnificationProcessor.setMagnificationConfig(displayId, config, animate,
+ mId);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 6473bf5ffc3e..327f08753524 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -84,6 +84,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@GuardedBy("mLock")
private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ @GuardedBy("mLock")
+ private int mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
// Track the active user to reset the magnification and get the associated user settings.
private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
@GuardedBy("mLock")
@@ -239,6 +241,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
disableFullScreenMagnificationIfNeeded(displayId);
@@ -276,6 +279,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
} else {
@@ -298,6 +302,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
/**
+ * Returns the last activated magnification mode. If there is no activated magnifier before, it
+ * returns fullscreen mode by default.
+ */
+ public int getLastActivatedMode() {
+ synchronized (mLock) {
+ return mLastActivatedMode;
+ }
+ }
+
+ /**
* Wrapper method of logging the magnification activated mode and its duration of the usage
* when the magnification is disabled.
*
@@ -336,6 +350,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
fullMagnificationController = mFullScreenMagnificationController;
windowMagnificationManager = mWindowMagnificationMgr;
+ mLastActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
}
mScaleProvider.onUserChanged(userId);
@@ -462,7 +477,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
return mTempPoint;
}
- private boolean isActivated(int displayId, int mode) {
+ /**
+ * Return {@code true} if the specified magnification mode on the given display is activated
+ * or not.
+ *
+ * @param displayId The logical displayId.
+ * @param mode It's either ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN or
+ * ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW.
+ */
+ public boolean isActivated(int displayId, int mode) {
boolean isActivated = false;
if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index efc6d515fdb8..2324a5a781f4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -16,6 +16,13 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.DEFAULT_MODE;
+import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
+import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.graphics.Region;
@@ -23,6 +30,22 @@ import android.graphics.Region;
* Processor class for AccessibilityService connection to control magnification on the specified
* display. This wraps the function of magnification controller.
*
+ * <p>
+ * If the magnification config uses {@link DEFAULT_MODE}. This processor will control the current
+ * activated magnifier on the display. If there is no magnifier activated, it controls
+ * full-screen magnifier by default.
+ * </p>
+ *
+ * <p>
+ * If the magnification config uses {@link FULLSCREEN_MODE}. This processor will control
+ * full-screen magnifier on the display.
+ * </p>
+ *
+ * <p>
+ * If the magnification config uses {@link WINDOW_MODE}. This processor will control
+ * the activated window magnifier on the display.
+ * </p>
+ *
* @see MagnificationController
* @see FullScreenMagnificationController
*/
@@ -35,53 +58,166 @@ public class MagnificationProcessor {
}
/**
- * {@link FullScreenMagnificationController#getScale(int)}
+ * Gets the magnification config of the display.
+ *
+ * @param displayId The logical display id
+ * @return the magnification config
+ */
+ public @NonNull MagnificationConfig getMagnificationConfig(int displayId) {
+ final int mode = getControllingMode(displayId);
+ MagnificationConfig.Builder builder = new MagnificationConfig.Builder();
+ if (mode == FULLSCREEN_MODE) {
+ final FullScreenMagnificationController fullScreenMagnificationController =
+ mController.getFullScreenMagnificationController();
+ builder.setMode(mode)
+ .setScale(fullScreenMagnificationController.getScale(displayId))
+ .setCenterX(fullScreenMagnificationController.getCenterX(displayId))
+ .setCenterY(fullScreenMagnificationController.getCenterY(displayId));
+ } else if (mode == WINDOW_MODE) {
+ final WindowMagnificationManager windowMagnificationManager =
+ mController.getWindowMagnificationMgr();
+ builder.setMode(mode)
+ .setScale(windowMagnificationManager.getScale(displayId))
+ .setCenterX(windowMagnificationManager.getCenterX(displayId))
+ .setCenterY(windowMagnificationManager.getCenterY(displayId));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets the magnification config of the display. If animation is disabled, the transition
+ * is immediate.
+ *
+ * @param displayId The logical display id
+ * @param config The magnification config
+ * @param animate {@code true} to animate from the current config or
+ * {@code false} to set the config immediately
+ * @param id The ID of the service requesting the change
+ * @return {@code true} if the magnification spec changed, {@code false} if the spec did not
+ * change
+ */
+ public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config,
+ boolean animate, int id) {
+ int configMode = config.getMode();
+ if (configMode == DEFAULT_MODE) {
+ configMode = getControllingMode(displayId);
+ }
+ if (configMode == FULLSCREEN_MODE) {
+ return setScaleAndCenterForFullScreenMagnification(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY(),
+ animate, id);
+ } else if (configMode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
+ config.getScale(), config.getCenterX(), config.getCenterY());
+ }
+ return false;
+ }
+
+ /**
+ * Returns the magnification scale. If an animation is in progress,
+ * this reflects the end state of the animation.
+ *
+ * @param displayId The logical display id.
+ * @return the scale
*/
public float getScale(int displayId) {
- return mController.getFullScreenMagnificationController().getScale(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().getScale(displayId);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getScale(displayId);
+ }
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getCenterX(int)}
+ * Returns the magnification center in X coordinate of the controlling magnification mode.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make the
+ * magnification center correct.
+ *
+ * @param displayId The logical display id
+ * @param canControlMagnification Whether the service can control magnification
+ * @return the X coordinate
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterX(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterX(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
+ }
}
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getCenterY(int)}
+ * Returns the magnification center in Y coordinate of the controlling magnification mode.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make the
+ * magnification center correct.
+ *
+ * @param displayId The logical display id
+ * @param canControlMagnification Whether the service can control magnification
+ * @return the Y coordinate
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterY(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterY(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
+ }
}
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
+ return 0;
}
/**
- * {@link FullScreenMagnificationController#getMagnificationRegion(int, Region)}
+ * Return the magnification bounds of the current controlling magnification on the given
+ * display. If the magnifier is not enabled, it returns an empty region.
+ * If the service can control magnification but fullscreen magnifier is not registered, it will
+ * register the magnifier for this call then unregister the magnifier finally to make
+ * the magnification region correct.
+ *
+ * @param displayId The logical display id
+ * @param outRegion the region to populate
+ * @param canControlMagnification Whether the service can control magnification
+ * @return outRegion the magnification bounds of full-screen magnifier or the magnification
+ * source bounds of window magnifier
*/
public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
- boolean registeredJustForThisCall = registerMagnificationIfNeeded(displayId,
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
+ } else if (mode == WINDOW_MODE) {
+ mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ outRegion);
+ }
+ return outRegion;
+ }
+
+ private void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
+ boolean canControlMagnification) {
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
try {
mController.getFullScreenMagnificationController().getMagnificationRegion(displayId,
outRegion);
- return outRegion;
} finally {
if (registeredJustForThisCall) {
unregister(displayId);
@@ -89,67 +225,105 @@ public class MagnificationProcessor {
}
}
- /**
- * {@link FullScreenMagnificationController#setScaleAndCenter(int, float, float, float, boolean,
- * int)}
- */
- public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
+ private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
+ float centerX, float centerY,
boolean animate, int id) {
if (!isRegistered(displayId)) {
register(displayId);
}
- return mController.getFullScreenMagnificationController().setScaleAndCenter(displayId,
+ return mController.getFullScreenMagnificationController().setScaleAndCenter(
+ displayId,
scale,
centerX, centerY, animate, id);
}
/**
- * {@link FullScreenMagnificationController#reset(int, boolean)}
+ * Resets the magnification on the given display. The reset mode could be full-screen or
+ * window if it is activated.
+ *
+ * @param displayId The logical display id.
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
*/
public boolean reset(int displayId, boolean animate) {
- return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().reset(displayId);
+ }
+ return false;
}
/**
* {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
*/
+ // TODO: support window magnification
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
}
/**
- * {@link FullScreenMagnificationController#register(int)}
- */
- public void register(int displayId) {
- mController.getFullScreenMagnificationController().register(displayId);
- }
-
- /**
- * {@link FullScreenMagnificationController#unregister(int)} (int)}
- */
- public void unregister(int displayId) {
- mController.getFullScreenMagnificationController().unregister(displayId);
- }
-
- /**
* {@link FullScreenMagnificationController#isMagnifying(int)}
+ * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
*/
public boolean isMagnifying(int displayId) {
- return mController.getFullScreenMagnificationController().isMagnifying(displayId);
+ int mode = getControllingMode(displayId);
+ if (mode == FULLSCREEN_MODE) {
+ return mController.getFullScreenMagnificationController().isMagnifying(displayId);
+ } else if (mode == WINDOW_MODE) {
+ return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+ }
+ return false;
}
/**
- * {@link FullScreenMagnificationController#isRegistered(int)}
+ * Returns the current controlling magnification mode on the given display.
+ * If there is no magnifier activated, it fallbacks to the last activated mode.
+ * And the last activated mode is {@link FULLSCREEN_MODE} by default.
+ *
+ * @param displayId The logical display id
*/
- public boolean isRegistered(int displayId) {
- return mController.getFullScreenMagnificationController().isRegistered(displayId);
+ public int getControllingMode(int displayId) {
+ if (mController.isActivated(displayId,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
+ return WINDOW_MODE;
+ } else if (mController.isActivated(displayId,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
+ return FULLSCREEN_MODE;
+ } else {
+ return (mController.getLastActivatedMode() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
+ ? WINDOW_MODE
+ : FULLSCREEN_MODE;
+ }
}
- private boolean registerMagnificationIfNeeded(int displayId, boolean canControlMagnification) {
+ private boolean registerDisplayMagnificationIfNeeded(int displayId,
+ boolean canControlMagnification) {
if (!isRegistered(displayId) && canControlMagnification) {
register(displayId);
return true;
}
return false;
}
+
+ private boolean isRegistered(int displayId) {
+ return mController.getFullScreenMagnificationController().isRegistered(displayId);
+ }
+
+ /**
+ * {@link FullScreenMagnificationController#register(int)}
+ */
+ private void register(int displayId) {
+ mController.getFullScreenMagnificationController().register(displayId);
+ }
+
+ /**
+ * {@link FullScreenMagnificationController#unregister(int)} (int)}
+ */
+ private void unregister(int displayId) {
+ mController.getFullScreenMagnificationController().unregister(displayId);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index e1ec273b444a..d34b4a9f3ca7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -271,9 +272,12 @@ public class WindowMagnificationManager implements
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
+ * @return {@code true} if the magnification is enabled successfully.
*/
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
- enableWindowMagnification(displayId, scale, centerX, centerY, STUB_ANIMATION_CALLBACK);
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY) {
+ return enableWindowMagnification(displayId, scale, centerX, centerY,
+ STUB_ANIMATION_CALLBACK);
}
/**
@@ -287,25 +291,29 @@ public class WindowMagnificationManager implements
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param animationCallback Called when the animation result is valid.
+ * @return {@code true} if the magnification is enabled successfully.
*/
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable MagnificationAnimationCallback animationCallback) {
+ public boolean enableWindowMagnification(int displayId, float scale, float centerX,
+ float centerY, @Nullable MagnificationAnimationCallback animationCallback) {
final boolean enabled;
+ boolean previousEnabled;
synchronized (mLock) {
if (mConnectionWrapper == null) {
- return;
+ return false;
}
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
+ previousEnabled = magnifier.mEnabled;
enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
animationCallback);
}
- if (enabled) {
+ if (enabled && !previousEnabled) {
mCallback.onWindowMagnificationActivationState(displayId, true);
}
+ return enabled;
}
/**
@@ -464,7 +472,7 @@ public class WindowMagnificationManager implements
* @param displayId The logical display id
* @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
*/
- float getCenterX(int displayId) {
+ public float getCenterX(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -480,7 +488,7 @@ public class WindowMagnificationManager implements
* @param displayId The logical display id
* @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
*/
- float getCenterY(int displayId) {
+ public float getCenterY(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -491,6 +499,42 @@ public class WindowMagnificationManager implements
}
/**
+ * Populates magnified bounds on the screen. And the populated magnified bounds would be
+ * empty If window magnifier is not activated.
+ *
+ * @param displayId The logical display id.
+ * @param outRegion the region to populate
+ */
+ public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ outRegion.setEmpty();
+ } else {
+ outRegion.set(magnifier.mSourceBounds);
+ }
+ }
+ }
+
+ /**
+ * Resets the magnification scale and center.
+ *
+ * @param displayId The logical display id.
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
+ */
+ public boolean reset(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ magnifier.reset();
+ return true;
+ }
+ }
+
+ /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -626,8 +670,9 @@ public class WindowMagnificationManager implements
@GuardedBy("mLock")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback) {
- if (mEnabled) {
- return false;
+ // Handle defaults. The scale may be NAN when just updating magnification center.
+ if (Float.isNaN(scale)) {
+ scale = getScale();
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index c9608a55170e..3e0208411c21 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,24 +20,27 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.net.NetworkStack;
import android.net.Uri;
import android.net.nsd.INsdManager;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Message;
-import android.os.Messenger;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Base64;
+import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -72,12 +75,11 @@ public class NsdService extends INsdManager.Stub {
/**
* Clients receiving asynchronous messages
*/
- private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>();
+ private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>();
/* A map from unique id to client info */
private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>();
- private final AsyncChannel mReplyChannel = new AsyncChannel();
private final long mCleanupDelayMs;
private static final int INVALID_ID = 0;
@@ -149,65 +151,66 @@ public class NsdService extends INsdManager.Stub {
class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
- ClientInfo cInfo = null;
+ final ClientInfo cInfo;
+ final int clientId = msg.arg2;
switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- AsyncChannel c = (AsyncChannel) msg.obj;
- if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
- c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
- cInfo = new ClientInfo(c, msg.replyTo);
- mClients.put(msg.replyTo, cInfo);
- } else {
- Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ case NsdManager.REGISTER_CLIENT:
+ final Pair<NsdServiceConnector, INsdManagerCallback> arg =
+ (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj;
+ final INsdManagerCallback cb = arg.second;
+ try {
+ cb.asBinder().linkToDeath(arg.first, 0);
+ cInfo = new ClientInfo(cb);
+ mClients.put(arg.first, cInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Client " + clientId + " has already died");
}
break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- switch (msg.arg1) {
- case AsyncChannel.STATUS_SEND_UNSUCCESSFUL:
- Slog.e(TAG, "Send failed, client connection lost");
- break;
- case AsyncChannel.STATUS_REMOTE_DISCONNECTION:
- if (DBG) Slog.d(TAG, "Client disconnected");
- break;
- default:
- if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
- break;
- }
-
- cInfo = mClients.get(msg.replyTo);
+ case NsdManager.UNREGISTER_CLIENT:
+ final NsdServiceConnector connector = (NsdServiceConnector) msg.obj;
+ cInfo = mClients.remove(connector);
if (cInfo != null) {
cInfo.expungeAllRequests();
- mClients.remove(msg.replyTo);
if (cInfo.isLegacy()) {
mLegacyClientCount -= 1;
}
}
maybeScheduleStop();
break;
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, getHandler(), msg.replyTo);
- break;
case NsdManager.DISCOVER_SERVICES:
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.STOP_DISCOVERY:
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.REGISTER_SERVICE:
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.UNREGISTER_SERVICE:
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onUnregisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.RESOLVE_SERVICE:
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ cInfo = getClientInfoForReply(msg);
+ if (cInfo != null) {
+ cInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
+ }
break;
case NsdManager.DAEMON_CLEANUP:
mDaemon.maybeStop();
@@ -215,7 +218,7 @@ public class NsdService extends INsdManager.Stub {
// This event should be only sent by the legacy (target SDK < S) clients.
// Mark the sending client as legacy.
case NsdManager.DAEMON_STARTUP:
- cInfo = mClients.get(msg.replyTo);
+ cInfo = getClientInfoForReply(msg);
if (cInfo != null) {
cancelStop();
cInfo.setLegacy();
@@ -230,6 +233,11 @@ public class NsdService extends INsdManager.Stub {
}
return HANDLED;
}
+
+ private ClientInfo getClientInfoForReply(Message msg) {
+ final ListenerArgs args = (ListenerArgs) msg.obj;
+ return mClients.get(args.connector);
+ }
}
class DisabledState extends State {
@@ -289,122 +297,119 @@ public class NsdService extends INsdManager.Stub {
@Override
public boolean processMessage(Message msg) {
- ClientInfo clientInfo;
- NsdServiceInfo servInfo;
- int id;
+ final ClientInfo clientInfo;
+ final int id;
+ final int clientId = msg.arg2;
+ final ListenerArgs args;
switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- return NOT_HANDLED;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- return NOT_HANDLED;
case NsdManager.DISABLE:
//TODO: cleanup clients
transitionTo(mDisabledState);
break;
case NsdManager.DISCOVER_SERVICES:
if (DBG) Slog.d(TAG, "Discover services");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
+ clientInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (discoverServices(id, servInfo.getServiceType())) {
+ if (discoverServices(id, args.serviceInfo.getServiceType())) {
if (DBG) {
Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
- servInfo.getServiceType());
+ args.serviceInfo.getServiceType());
}
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
+ clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
} else {
stopServiceDiscovery(id);
- replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED,
+ clientInfo.onDiscoverServicesFailed(clientId,
NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.STOP_DISCOVERY:
if (DBG) Slog.d(TAG, "Stop service discovery");
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
try {
- id = clientInfo.mClientIds.get(msg.arg2);
+ id = clientInfo.mClientIds.get(clientId);
} catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
}
- removeRequestMap(msg.arg2, id, clientInfo);
+ removeRequestMap(clientId, id, clientInfo);
if (stopServiceDiscovery(id)) {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED);
+ clientInfo.onStopDiscoverySucceeded(clientId);
} else {
- replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onStopDiscoveryFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
- clientInfo = mClients.get(msg.replyTo);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) {
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_MAX_LIMIT);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_MAX_LIMIT);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (registerService(id, (NsdServiceInfo) msg.obj)) {
- if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
+ if (registerService(id, args.serviceInfo)) {
+ if (DBG) Slog.d(TAG, "Register " + clientId + " " + id);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
// Return success after mDns reports success
} else {
unregisterService(id);
- replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.UNREGISTER_SERVICE:
if (DBG) Slog.d(TAG, "unregister service");
- clientInfo = mClients.get(msg.replyTo);
- try {
- id = clientInfo.mClientIds.get(msg.arg2);
- } catch (NullPointerException e) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
+ if (clientInfo == null) {
+ Slog.e(TAG, "Unknown connector in unregistration");
break;
}
- removeRequestMap(msg.arg2, id, clientInfo);
+ id = clientInfo.mClientIds.get(clientId);
+ removeRequestMap(clientId, id, clientInfo);
if (unregisterService(id)) {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED);
+ clientInfo.onUnregisterServiceSucceeded(clientId);
} else {
- replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onUnregisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.RESOLVE_SERVICE:
if (DBG) Slog.d(TAG, "Resolve service");
- servInfo = (NsdServiceInfo) msg.obj;
- clientInfo = mClients.get(msg.replyTo);
-
+ args = (ListenerArgs) msg.obj;
+ clientInfo = mClients.get(args.connector);
if (clientInfo.mResolvedService != null) {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_ALREADY_ACTIVE);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_ALREADY_ACTIVE);
break;
}
maybeStartDaemon();
id = getUniqueId();
- if (resolveService(id, servInfo)) {
+ if (resolveService(id, args.serviceInfo)) {
clientInfo.mResolvedService = new NsdServiceInfo();
- storeRequestMap(msg.arg2, id, clientInfo, msg.what);
+ storeRequestMap(clientId, id, clientInfo, msg.what);
} else {
- replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
case NsdManager.NATIVE_DAEMON_EVENT:
@@ -449,30 +454,27 @@ public class NsdService extends INsdManager.Stub {
case NativeResponseCode.SERVICE_FOUND:
/* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0,
- clientId, servInfo);
+ clientInfo.onServiceFound(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_LOST:
/* NNN uniqueId serviceName regType domain */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
- clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0,
- clientId, servInfo);
+ clientInfo.onServiceLost(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
/* NNN uniqueId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onDiscoverServicesFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
servInfo = new NsdServiceInfo(cooked[2], null);
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
- id, clientId, servInfo);
+ clientInfo.onRegisterServiceSucceeded(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
/* NNN regId errorCode */
- clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onRegisterServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_UPDATED:
/* NNN regId */
@@ -511,8 +513,8 @@ public class NsdService extends INsdManager.Stub {
if (getAddrInfo(id2, cooked[3])) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientInfo.mResolvedService = null;
}
break;
@@ -521,26 +523,26 @@ public class NsdService extends INsdManager.Stub {
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
/* NNN resolveId hostname ttl addr */
try {
clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED,
- 0, clientId, clientInfo.mResolvedService);
+ clientInfo.onResolveServiceSucceeded(
+ clientId, clientInfo.mResolvedService);
} catch (java.net.UnknownHostException e) {
- clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED,
- NsdManager.FAILURE_INTERNAL_ERROR, clientId);
+ clientInfo.onResolveServiceFailed(
+ clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
@@ -601,15 +603,71 @@ public class NsdService extends INsdManager.Stub {
return service;
}
- public Messenger getMessenger() {
+ @Override
+ public INsdServiceConnector connect(INsdManagerCallback cb) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService");
- return new Messenger(mNsdStateMachine.getHandler());
+ final INsdServiceConnector connector = new NsdServiceConnector();
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb)));
+ return connector;
+ }
+
+ private static class ListenerArgs {
+ public final NsdServiceConnector connector;
+ public final NsdServiceInfo serviceInfo;
+ ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) {
+ this.connector = connector;
+ this.serviceInfo = serviceInfo;
+ }
}
- public void setEnabled(boolean isEnabled) {
- NetworkStack.checkNetworkStackPermission(mContext);
- mNsdSettings.putEnabledStatus(isEnabled);
- notifyEnabled(isEnabled);
+ private class NsdServiceConnector extends INsdServiceConnector.Stub
+ implements IBinder.DeathRecipient {
+ @Override
+ public void registerService(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.REGISTER_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void unregisterService(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.UNREGISTER_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.DISCOVER_SERVICES, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void stopDiscovery(int listenerKey) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.RESOLVE_SERVICE, 0, listenerKey,
+ new ListenerArgs(this, serviceInfo)));
+ }
+
+ @Override
+ public void startDaemon() {
+ mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage(
+ NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null)));
+ }
+
+ @Override
+ public void binderDied() {
+ mNsdStateMachine.sendMessage(
+ mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this));
+ }
}
private void notifyEnabled(boolean isEnabled) {
@@ -832,43 +890,11 @@ public class NsdService extends INsdManager.Stub {
mNsdStateMachine.dump(fd, pw, args);
}
- /* arg2 on the source message has an id that needs to be retained in replies
- * see NsdManager for details */
- private Message obtainMessage(Message srcMsg) {
- Message msg = Message.obtain();
- msg.arg2 = srcMsg.arg2;
- return msg;
- }
-
- private void replyToMessage(Message msg, int what) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, int arg1) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.arg1 = arg1;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
- private void replyToMessage(Message msg, int what, Object obj) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.obj = obj;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
-
/* Information tracked per client */
private class ClientInfo {
private static final int MAX_LIMIT = 10;
- private final AsyncChannel mChannel;
- private final Messenger mMessenger;
+ private final INsdManagerCallback mCb;
/* Remembers a resolved service until getaddrinfo completes */
private NsdServiceInfo mResolvedService;
@@ -881,17 +907,14 @@ public class NsdService extends INsdManager.Stub {
// The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsLegacy = false;
- private ClientInfo(AsyncChannel c, Messenger m) {
- mChannel = c;
- mMessenger = m;
- if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m);
+ private ClientInfo(INsdManagerCallback cb) {
+ mCb = cb;
+ if (DBG) Slog.d(TAG, "New client");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("mChannel ").append(mChannel).append("\n");
- sb.append("mMessenger ").append(mMessenger).append("\n");
sb.append("mResolvedService ").append(mResolvedService).append("\n");
sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
for(int i = 0; i< mClientIds.size(); i++) {
@@ -949,6 +972,102 @@ public class NsdService extends INsdManager.Stub {
}
return mClientIds.keyAt(idx);
}
+
+ void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onDiscoverServicesStarted(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onDiscoverServicesStarted", e);
+ }
+ }
+
+ void onDiscoverServicesFailed(int listenerKey, int error) {
+ try {
+ mCb.onDiscoverServicesFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onDiscoverServicesFailed", e);
+ }
+ }
+
+ void onServiceFound(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onServiceFound(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceFound(", e);
+ }
+ }
+
+ void onServiceLost(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onServiceLost(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onServiceLost(", e);
+ }
+ }
+
+ void onStopDiscoveryFailed(int listenerKey, int error) {
+ try {
+ mCb.onStopDiscoveryFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopDiscoveryFailed", e);
+ }
+ }
+
+ void onStopDiscoverySucceeded(int listenerKey) {
+ try {
+ mCb.onStopDiscoverySucceeded(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onStopDiscoverySucceeded", e);
+ }
+ }
+
+ void onRegisterServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onRegisterServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onRegisterServiceFailed", e);
+ }
+ }
+
+ void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onRegisterServiceSucceeded(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onRegisterServiceSucceeded", e);
+ }
+ }
+
+ void onUnregisterServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onUnregisterServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onUnregisterServiceFailed", e);
+ }
+ }
+
+ void onUnregisterServiceSucceeded(int listenerKey) {
+ try {
+ mCb.onUnregisterServiceSucceeded(listenerKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e);
+ }
+ }
+
+ void onResolveServiceFailed(int listenerKey, int error) {
+ try {
+ mCb.onResolveServiceFailed(listenerKey, error);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onResolveServiceFailed", e);
+ }
+ }
+
+ void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) {
+ try {
+ mCb.onResolveServiceSucceeded(listenerKey, info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling onResolveServiceSucceeded", e);
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index b019789e1982..ac20a08c1e30 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -685,6 +685,7 @@ public class Watchdog {
final UUID errorId = mTraceErrorLogger.generateErrorId();
if (mTraceErrorLogger.isAddErrorIdEnabled()) {
mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
+ mTraceErrorLogger.addSubjectToTrace(subject, errorId);
}
// Log the atom as early as possible since it is used as a mechanism to trigger
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ded115bbbcb4..02a16fc08c55 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -642,10 +642,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final BroadcastQueue mFgBroadcastQueue;
final BroadcastQueue mBgBroadcastQueue;
- final BroadcastQueue mOffloadBroadcastQueue;
+ final BroadcastQueue mBgOffloadBroadcastQueue;
+ final BroadcastQueue mFgOffloadBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
// so that dispatch of foreground broadcasts gets precedence.
- final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[3];
+ final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[4];
@GuardedBy("this")
BroadcastStats mLastBroadcastStats;
@@ -656,12 +657,20 @@ public class ActivityManagerService extends IActivityManager.Stub
TraceErrorLogger mTraceErrorLogger;
BroadcastQueue broadcastQueueForIntent(Intent intent) {
- if (isOnOffloadQueue(intent.getFlags())) {
+ if (isOnFgOffloadQueue(intent.getFlags())) {
if (DEBUG_BROADCAST_BACKGROUND) {
Slog.i(TAG_BROADCAST,
- "Broadcast intent " + intent + " on offload queue");
+ "Broadcast intent " + intent + " on foreground offload queue");
}
- return mOffloadBroadcastQueue;
+ return mFgOffloadBroadcastQueue;
+ }
+
+ if (isOnBgOffloadQueue(intent.getFlags())) {
+ if (DEBUG_BROADCAST_BACKGROUND) {
+ Slog.i(TAG_BROADCAST,
+ "Broadcast intent " + intent + " on background offload queue");
+ }
+ return mBgOffloadBroadcastQueue;
}
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
@@ -2263,7 +2272,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mPendingStartActivityUids = new PendingStartActivityUids(mContext);
mUseFifoUiScheduling = false;
mEnableOffloadQueue = false;
- mFgBroadcastQueue = mBgBroadcastQueue = mOffloadBroadcastQueue = null;
+ mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue =
+ mFgOffloadBroadcastQueue = null;
mComponentAliasResolver = new ComponentAliasResolver(this);
}
@@ -2324,11 +2334,14 @@ public class ActivityManagerService extends IActivityManager.Stub
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
- mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
- "offload", offloadConstants, true);
+ mBgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ "offload_bg", offloadConstants, true);
+ mFgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ "offload_fg", foreConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
- mBroadcastQueues[2] = mOffloadBroadcastQueue;
+ mBroadcastQueues[2] = mBgOffloadBroadcastQueue;
+ mBroadcastQueues[3] = mFgOffloadBroadcastQueue;
mServices = new ActiveServices(this);
mCpHelper = new ContentProviderHelper(this, true);
@@ -12552,13 +12565,15 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean isPendingBroadcastProcessLocked(int pid) {
return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
|| mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
- || mOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid);
+ || mBgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid)
+ || mFgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(pid);
}
boolean isPendingBroadcastProcessLocked(ProcessRecord app) {
return mFgBroadcastQueue.isPendingBroadcastProcessLocked(app)
|| mBgBroadcastQueue.isPendingBroadcastProcessLocked(app)
- || mOffloadBroadcastQueue.isPendingBroadcastProcessLocked(app);
+ || mBgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(app)
+ || mFgOffloadBroadcastQueue.isPendingBroadcastProcessLocked(app);
}
void skipPendingBroadcastLocked(int pid) {
@@ -12675,30 +12690,38 @@ public class ActivityManagerService extends IActivityManager.Stub
"Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+ "flag");
}
- if (CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
- callingUid)
- && !explicitExportStateDefined) {
- if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
- throw new SecurityException(
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
- } else {
- Slog.wtf(TAG,
- callerPackage + ": Targeting T+ (version "
- + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a "
- + "receiver");
- // Assume default behavior-- flag check is not enforced
+
+ // Don't enforce the flag check if we're EITHER registering for only protected
+ // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
+ // not be used generally, so we will be marking them as exported by default
+ final boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
+ DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+ if (!onlyProtectedBroadcasts) {
+ if (receiver == null && !explicitExportStateDefined) {
+ // sticky broadcast, no flag specified (flag isn't required)
+ flags |= Context.RECEIVER_EXPORTED;
+ } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
+ if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
+ throw new SecurityException(
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ } else {
+ Slog.wtf(TAG,
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ // Assume default behavior-- flag check is not enforced
+ flags |= Context.RECEIVER_EXPORTED;
+ }
+ } else if (!requireExplicitFlagForDynamicReceivers) {
+ // Change is not enabled, thus not targeting T+. Assume exported.
flags |= Context.RECEIVER_EXPORTED;
}
- } else if (!CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
- callingUid)) {
- // Change is not enabled, thus not targeting T+. Assume exported.
- flags |= Context.RECEIVER_EXPORTED;
}
}
@@ -12716,7 +12739,7 @@ public class ActivityManagerService extends IActivityManager.Stub
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
continue;
}
- // If intent has scheme "content", it will need to acccess
+ // If intent has scheme "content", it will need to access
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
@@ -14007,8 +14030,10 @@ public class ActivityManagerService extends IActivityManager.Stub
BroadcastQueue queue;
synchronized(this) {
- if (isOnOffloadQueue(flags)) {
- queue = mOffloadBroadcastQueue;
+ if (isOnFgOffloadQueue(flags)) {
+ queue = mFgOffloadBroadcastQueue;
+ } else if (isOnBgOffloadQueue(flags)) {
+ queue = mBgOffloadBroadcastQueue;
} else {
queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
@@ -15392,6 +15417,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public String getSwitchingFromUserMessage() {
+ return mUserController.getSwitchingFromSystemUserMessage();
+ }
+
+ @Override
+ public String getSwitchingToUserMessage() {
+ return mUserController.getSwitchingToSystemUserMessage();
+ }
+
+ @Override
public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
mUserController.setStopUserOnSwitch(value);
}
@@ -16302,7 +16337,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ intent.addFlags(Intent.FLAG_RECEIVER_OFFLOAD_FOREGROUND
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
if (initLocale || !mProcessesReady) {
@@ -17479,7 +17514,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private boolean isOnOffloadQueue(int flags) {
+ private boolean isOnFgOffloadQueue(int flags) {
+ return ((flags & Intent.FLAG_RECEIVER_OFFLOAD_FOREGROUND) != 0);
+ }
+
+ private boolean isOnBgOffloadQueue(int flags) {
return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 17daa753100c..592abbbb5747 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -789,7 +789,7 @@ public final class BroadcastQueue {
// Ensure that broadcasts are only sent to other apps if they are explicitly marked as
// exported, or are System level broadcasts
- if (!skip && !filter.exported && Process.SYSTEM_UID != r.callingUid
+ if (!skip && !filter.exported && !Process.isCoreUid(r.callingUid)
&& filter.receiverList.uid != r.callingUid) {
Slog.w(TAG, "Exported Denial: sending "
@@ -800,7 +800,7 @@ public final class BroadcastQueue {
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- skip = true;
+ // skip = true;
}
if (skip) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 42205060e5d5..18ad1f557ee6 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -286,6 +286,7 @@ class ProcessErrorStateRecord {
&& mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mService.mTraceErrorLogger.generateErrorId();
mService.mTraceErrorLogger.addErrorIdToTrace(mApp.processName, errorId);
+ mService.mTraceErrorLogger.addSubjectToTrace(annotation, errorId);
} else {
errorId = null;
}
diff --git a/services/core/java/com/android/server/am/TraceErrorLogger.java b/services/core/java/com/android/server/am/TraceErrorLogger.java
index c65810097433..29a9b5c501c4 100644
--- a/services/core/java/com/android/server/am/TraceErrorLogger.java
+++ b/services/core/java/com/android/server/am/TraceErrorLogger.java
@@ -54,4 +54,17 @@ public class TraceErrorLogger {
COUNTER_PREFIX + processName + "#" + errorId.toString(),
PLACEHOLDER_VALUE);
}
+
+ /**
+ * Pushes a counter containing an ANR/Watchdog subject and a unique id so that the subject
+ * can be uniquely identified.
+ *
+ * @param subject The subject to include in the trace.
+ * @param errorId The unique id with which to tag the trace.
+ */
+ public void addSubjectToTrace(String subject, UUID errorId) {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ String.format("Subject(for ErrorId %s):%s", errorId.toString(), subject),
+ PLACEHOLDER_VALUE);
+ }
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d52f52e59d39..0bab0235d718 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1809,7 +1809,8 @@ class UserController implements Handler.Callback {
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
- getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
+ getSwitchingFromSystemUserMessageUnchecked(),
+ getSwitchingToSystemUserMessageUnchecked());
}
private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
@@ -2622,18 +2623,40 @@ class UserController implements Handler.Callback {
}
}
- private String getSwitchingFromSystemUserMessage() {
+ // Called by AMS, must check permission
+ String getSwitchingFromSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
+
+ return getSwitchingFromSystemUserMessageUnchecked();
+ }
+
+ // Called by AMS, must check permission
+ String getSwitchingToSystemUserMessage() {
+ checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
+
+ return getSwitchingToSystemUserMessageUnchecked();
+ }
+
+ private String getSwitchingFromSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingFromSystemUserMessage;
}
}
- private String getSwitchingToSystemUserMessage() {
+ private String getSwitchingToSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingToSystemUserMessage;
}
}
+ private void checkHasManageUsersPermission(String operation) {
+ if (mInjector.checkCallingPermission(
+ android.Manifest.permission.MANAGE_USERS) == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "You need MANAGE_USERS permission to call " + operation);
+ }
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
long token = proto.start(fieldId);
@@ -2706,6 +2729,12 @@ class UserController implements Handler.Callback {
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
+ if (mSwitchingFromSystemUserMessage != null) {
+ pw.println(" mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
+ }
+ if (mSwitchingToSystemUserMessage != null) {
+ pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e4bed3d06714..1c62699f349c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -110,6 +110,7 @@ import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -239,6 +240,10 @@ public final class DisplayManagerService extends SystemService {
public final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
+ /** All {@link DisplayWindowPolicyController}s indexed by {@link DisplayInfo#displayId}. */
+ final SparseArray<DisplayWindowPolicyController> mDisplayWindowPolicyController =
+ new SparseArray<>();
+
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1117,7 +1122,8 @@ public final class DisplayManagerService extends SystemService {
private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
IMediaProjection projection, int callingUid, String packageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig) {
+ int flags, VirtualDisplayConfig virtualDisplayConfig,
+ DisplayWindowPolicyController controller) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
Slog.w(TAG, "Rejecting request to create private virtual display "
@@ -1145,6 +1151,9 @@ public final class DisplayManagerService extends SystemService {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display != null) {
+ if (controller != null) {
+ mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller);
+ }
return display.getDisplayIdLocked();
}
@@ -1188,6 +1197,10 @@ public final class DisplayManagerService extends SystemService {
DisplayDevice device =
mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
if (device != null) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
+ if (display != null) {
+ mDisplayWindowPolicyController.delete(display.getDisplayIdLocked());
+ }
// TODO: multi-display - handle virtual displays the same as other display adapters.
mDisplayDeviceRepo.onDisplayDeviceEvent(device,
DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
@@ -2139,6 +2152,15 @@ public final class DisplayManagerService extends SystemService {
}
pw.println();
mPersistentDataStore.dump(pw);
+
+ final int displayWindowPolicyControllerCount = mDisplayWindowPolicyController.size();
+ pw.println();
+ pw.println("Display Window Policy Controllers: size="
+ + displayWindowPolicyControllerCount);
+ for (int i = 0; i < displayWindowPolicyControllerCount; i++) {
+ pw.print("Display " + mDisplayWindowPolicyController.keyAt(i) + ":");
+ mDisplayWindowPolicyController.valueAt(i).dump(" ", pw);
+ }
}
pw.println();
mDisplayModeDirector.dump(pw);
@@ -2704,6 +2726,13 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) {
+ return createVirtualDisplay(virtualDisplayConfig, callback, projection, packageName,
+ null /* controller */);
+ }
+
+ public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig,
+ IVirtualDisplayCallback callback, IMediaProjection projection, String packageName,
+ DisplayWindowPolicyController controller) {
final int callingUid = Binder.getCallingUid();
if (!validatePackageName(callingUid, packageName)) {
throw new SecurityException("packageName must match the calling uid");
@@ -2803,7 +2832,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
- surface, flags, virtualDisplayConfig);
+ surface, flags, virtualDisplayConfig, controller);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3624,6 +3653,13 @@ public final class DisplayManagerService extends SystemService {
public void onEarlyInteractivityChange(boolean interactive) {
mLogicalDisplayMapper.onEarlyInteractivityChange(interactive);
}
+
+ @Override
+ public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) {
+ synchronized (mSyncRoot) {
+ return mDisplayWindowPolicyController.get(displayId);
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index ffeaad173fca..457c2fd6fc41 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -154,10 +154,10 @@ public class LocaleManagerService extends SystemService {
final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
userId);
- boolean isSuccess = updater.setLocales(locales).commit();
+ boolean isConfigChanged = updater.setLocales(locales).commit();
//We want to send the broadcasts only if config was actually updated on commit.
- if (isSuccess) {
+ if (isConfigChanged) {
notifyAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyInstallerOfAppWhoseLocaleChanged(appPackageName, userId, locales);
notifyRegisteredReceivers(appPackageName, userId, locales);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bde554323a73..e117cc6f7a22 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6822,13 +6822,11 @@ public class NotificationManagerService extends SystemService {
// blocked apps
- boolean isMediaNotification = n.isMediaNotification()
- && n.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
- if (isBlocked && !isMediaNotification) {
+ if (isBlocked && !n.isMediaNotification()) {
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+ " by user request.");
@@ -7217,10 +7215,8 @@ public class NotificationManagerService extends SystemService {
final StatusBarNotification n = r.getSbn();
final Notification notification = n.getNotification();
- boolean isMediaNotification = notification.isMediaNotification()
- && notification.extras.getParcelable(
- Notification.EXTRA_MEDIA_SESSION) != null;
- if (!isMediaNotification && (appBanned || isRecordBlockedLocked(r))) {
+ if (!notification.isMediaNotification()
+ && (appBanned || isRecordBlockedLocked(r))) {
mUsageStats.registerBlocked(r);
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + pkg);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 39ed9c2d51d3..6622a778ec44 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2050,6 +2050,7 @@ final class InstallPackageHelper {
}
}
+ final String packageName = pkg.getPackageName();
for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
final String filePath = entry.getKey();
final String signaturePath = entry.getValue();
@@ -2077,10 +2078,13 @@ final class InstallPackageHelper {
try {
// A file may already have fs-verity, e.g. when reused during a split
// install. If the measurement succeeds, no need to attempt to set up.
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath,
+ rootHash);
} catch (Installer.InstallerException e) {
- mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize());
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ mPm.mInstaller.installApkVerity(packageName, filePath, fd,
+ result.getContentSize());
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, filePath,
+ rootHash);
}
} finally {
IoUtils.closeQuietly(fd);
@@ -2349,10 +2353,9 @@ final class InstallPackageHelper {
// Set install reason for users that are having the package newly installed.
final int[] allUsersList = mPm.mUserManager.getUserIds();
if (userId == UserHandle.USER_ALL) {
- // TODO(b/152629990): It appears that the package doesn't actually get newly
- // installed in this case, so the installReason shouldn't get modified?
for (int currentUserId : allUsersList) {
- if (!previousUserIds.contains(currentUserId)) {
+ if (!previousUserIds.contains(currentUserId)
+ && ps.getInstalled(currentUserId)) {
ps.setInstallReason(installReason, currentUserId);
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c8bd2c0bcecf..a3803447083a 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -497,7 +497,7 @@ public class Installer extends SystemService {
*
* @throws InstallerException if {@code dexopt} fails.
*/
- public boolean dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
+ public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String classLoaderContext,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
@@ -585,11 +585,14 @@ public class Installer extends SystemService {
}
}
- public void rmPackageDir(String packageDir) throws InstallerException {
+ /**
+ * Remove a directory belonging to a package.
+ */
+ public void rmPackageDir(String packageName, String packageDir) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(packageDir);
try {
- mInstalld.rmPackageDir(packageDir);
+ mInstalld.rmPackageDir(packageName, packageDir);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -662,35 +665,44 @@ public class Installer extends SystemService {
}
}
- public void createOatDir(String oatDir, String dexInstructionSet)
+ /**
+ * Creates an oat dir for given package and instruction set.
+ */
+ public void createOatDir(String packageName, String oatDir, String dexInstructionSet)
throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.createOatDir(oatDir, dexInstructionSet);
+ mInstalld.createOatDir(packageName, oatDir, dexInstructionSet);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void linkFile(String relativePath, String fromBase, String toBase)
+ /**
+ * Creates a hardlink for a path.
+ */
+ public void linkFile(String packageName, String relativePath, String fromBase, String toBase)
throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(fromBase);
BlockGuard.getVmPolicy().onPathAccess(toBase);
try {
- mInstalld.linkFile(relativePath, fromBase, toBase);
+ mInstalld.linkFile(packageName, relativePath, fromBase, toBase);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void moveAb(String apkPath, String instructionSet, String outputPath)
+ /**
+ * Moves oat/vdex/art from "B" set defined by ro.boot.slot_suffix to the default set.
+ */
+ public void moveAb(String packageName, String apkPath, String instructionSet, String outputPath)
throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
- mInstalld.moveAb(apkPath, instructionSet, outputPath);
+ mInstalld.moveAb(packageName, apkPath, instructionSet, outputPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
@@ -700,35 +712,41 @@ public class Installer extends SystemService {
* Deletes the optimized artifacts generated by ART and returns the number
* of freed bytes.
*/
- public long deleteOdex(String apkPath, String instructionSet, String outputPath)
- throws InstallerException {
+ public long deleteOdex(String packageName, String apkPath, String instructionSet,
+ String outputPath) throws InstallerException {
if (!checkBeforeRemote()) return -1;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
- return mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
+ return mInstalld.deleteOdex(packageName, apkPath, instructionSet, outputPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
- throws InstallerException {
+ /**
+ * Enables legacy apk-verity for an apk.
+ */
+ public void installApkVerity(String packageName, String filePath, FileDescriptor verityInput,
+ int contentSize) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
- mInstalld.installApkVerity(filePath, verityInput, contentSize);
+ mInstalld.installApkVerity(packageName, filePath, verityInput, contentSize);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
- public void assertFsverityRootHashMatches(String filePath, @NonNull byte[] expectedHash)
- throws InstallerException {
+ /**
+ * Checks if provided hash matches the file's fs-verity merkle tree root hash.
+ */
+ public void assertFsverityRootHashMatches(String packageName, String filePath,
+ @NonNull byte[] expectedHash) throws InstallerException {
if (!checkBeforeRemote()) return;
BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
- mInstalld.assertFsverityRootHashMatches(filePath, expectedHash);
+ mInstalld.assertFsverityRootHashMatches(packageName, filePath, expectedHash);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 9e6f4f7b75d2..c125fe10e899 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -411,6 +411,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
final List<String> paths =
AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ final String packageName = pkg.getPackageName();
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
String oatDir = PackageDexOptimizer.getOatDir(
@@ -420,7 +421,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
packagePaths++;
try {
- installer.moveAb(path, dexCodeInstructionSet, oatDir);
+ installer.moveAb(packageName, path, dexCodeInstructionSet, oatDir);
pathsSuccessful++;
} catch (InstallerException e) {
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 803a2833dc3c..8f14cf8787f3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2402,6 +2402,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = stageDir;
+ final String tempPackageName = toDir.getName();
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -2411,7 +2412,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (isLinkPossible(fromFiles, toDir)) {
if (!mResolvedInstructionSets.isEmpty()) {
final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
+ createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
}
// pre-create lib dirs for linking if necessary
if (!mResolvedNativeLibPaths.isEmpty()) {
@@ -2434,7 +2435,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
new File(libDir, archDirPath));
}
}
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ linkFiles(tempPackageName, fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
@@ -3529,18 +3530,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IOException("File: " + pathStr + " outside base: " + baseStr);
}
- private void createOatDirs(List<String> instructionSets, File fromDir)
+ private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
throws PackageManagerException {
for (String instructionSet : instructionSets) {
try {
- mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
} catch (InstallerException e) {
throw PackageManagerException.from(e);
}
}
}
- private void linkFile(String relativePath, String fromBase, String toBase) throws IOException {
+ private void linkFile(String packageName, String relativePath, String fromBase, String toBase)
+ throws IOException {
try {
// Try
final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
@@ -3548,21 +3550,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
fromBase, toBase)) {
return;
}
- mInstaller.linkFile(relativePath, fromBase, toBase);
+ mInstaller.linkFile(packageName, relativePath, fromBase, toBase);
} catch (InstallerException | IOException e) {
throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
+ fromBase + ", " + toBase + ")", e);
}
}
- private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
+ private void linkFiles(String packageName, List<File> fromFiles, File toDir, File fromDir)
throws IOException {
for (File fromFile : fromFiles) {
final String relativePath = getRelativePath(fromFile, fromDir);
final String fromBase = fromDir.getAbsolutePath();
final String toBase = toDir.getAbsolutePath();
- linkFile(relativePath, fromBase, toBase);
+ linkFile(packageName, relativePath, fromBase, toBase);
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
@@ -4299,7 +4301,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
incrementalFileStorages.cleanUpAndMarkComplete();
}
if (stageDir != null) {
- mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ final String tempPackageName = stageDir.getName();
+ mInstaller.rmPackageDir(tempPackageName, stageDir.getAbsolutePath());
}
} catch (InstallerException ignored) {
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 27288b9f5343..747bbfa57bd1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1500,16 +1500,19 @@ public class PackageManagerService extends IPackageManager.Stub
final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
synchronized (m.mInstallLock) {
final AndroidPackage pkg;
+ final PackageSetting ps;
final SharedUserSetting sharedUser;
final String oldSeInfo;
- final PackageStateInternal packageState = m.getPackageStateInternal(packageName);
- if (packageState == null) {
- Slog.e(TAG, "Failed to find package setting " + packageName);
- return;
+ synchronized (m.mLock) {
+ ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
+ }
+ pkg = ps.getPkg();
+ sharedUser = ps.getSharedUser();
+ oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
}
- pkg = packageState.getPkg();
- sharedUser = packageState.getSharedUser();
- oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, packageState);
if (pkg == null) {
Slog.e(TAG, "Failed to find package " + packageName);
@@ -1521,7 +1524,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!newSeInfo.equals(oldSeInfo)) {
Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ oldSeInfo + " to: " + newSeInfo);
- packageState.getTransientState().setOverrideSeInfo(newSeInfo);
+ ps.getPkgState().setOverrideSeInfo(newSeInfo);
m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
}
}
@@ -6848,24 +6851,24 @@ public class PackageManagerService extends IPackageManager.Stub
}
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
true /* checkShell */, "stop package");
- boolean shouldUnhibernate = false;
// writer
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getStopped(userId) && !stopped) {
- shouldUnhibernate = true;
- }
if (!shouldFilterApplication(ps, callingUid, userId)
&& mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
- if (shouldUnhibernate) {
+ // If this would cause the app to leave force-stop, then also make sure to unhibernate the
+ // app if needed.
+ if (!stopped) {
mHandler.post(() -> {
AppHibernationManagerInternal ah =
mInjector.getLocalService(AppHibernationManagerInternal.class);
- ah.setHibernatingForUser(packageName, userId, false);
- ah.setHibernatingGlobally(packageName, false);
+ if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
+ ah.setHibernatingForUser(packageName, userId, false);
+ ah.setHibernatingGlobally(packageName, false);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 3a2ac1c9b6df..48b893bda546 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -94,9 +94,10 @@ final class RemovePackageHelper {
}
}
- mInstaller.rmPackageDir(codePath.getAbsolutePath());
+ final String packageName = codePath.getName();
+ mInstaller.rmPackageDir(packageName, codePath.getAbsolutePath());
if (needRemoveParent) {
- mInstaller.rmPackageDir(codePathParent.getAbsolutePath());
+ mInstaller.rmPackageDir(packageName, codePathParent.getAbsolutePath());
removeCachedResult(codePathParent);
}
} catch (Installer.InstallerException e) {
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 6cc94ce1f657..9b08ef9b3525 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -894,14 +894,15 @@ final class ScanPackageHelper {
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+ final String packageName = pkg.getPackageName();
+ if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
return false;
}
// TODO: Allow base and splits to be verified individually.
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+ if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
return false;
}
}
@@ -914,7 +915,7 @@ final class ScanPackageHelper {
* whether the apk contains signed root hash. Note that the signer's certificate still needs to
* match one in a trusted source, and should be done separately.
*/
- private boolean canSkipForcedApkVerification(String apkPath) {
+ private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
return VerityUtils.hasFsverity(apkPath);
}
@@ -926,7 +927,8 @@ final class ScanPackageHelper {
}
synchronized (mPm.mInstallLock) {
// Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
+ rootHashObserved);
return true;
}
} catch (Installer.InstallerException | IOException | DigestException
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 302826ff1294..40d884598ceb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,8 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.Manifest;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.annotation.ColorRes;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
@@ -85,6 +87,7 @@ import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -3512,6 +3515,39 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ @Override
+ public UserHandle createUserWithAttributes(
+ String userName, String userType, @UserInfoFlag int flags,
+ Bitmap userIcon,
+ String accountName, String accountType, PersistableBundle accountOptions) {
+ checkManageOrCreateUsersPermission(flags);
+
+ if (someUserHasAccountNoChecks(accountName, accountType)) {
+ throw new ServiceSpecificException(
+ UserManager.USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS);
+ }
+
+ UserInfo userInfo;
+ try {
+ userInfo = createUserInternal(userName, userType, flags,
+ UserHandle.USER_NULL, null);
+
+ if (userInfo == null) {
+ throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN);
+ }
+ } catch (UserManager.CheckedUserOperationException e) {
+ throw e.toServiceSpecificException();
+ }
+
+ if (userIcon != null) {
+ mLocalService.setUserIcon(userInfo.id, userIcon);
+ }
+
+ setSeedAccountDataNoChecks(userInfo.id, accountName, accountType, accountOptions, true);
+
+ return userInfo.getUserHandle();
+ }
+
private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags, @UserIdInt int parentId,
@Nullable String[] disallowedPackages)
@@ -4934,7 +4970,12 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public void setSeedAccountData(@UserIdInt int userId, String accountName, String accountType,
PersistableBundle accountOptions, boolean persist) {
- checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data");
+ checkManageUsersPermission("set user seed account data");
+ setSeedAccountDataNoChecks(userId, accountName, accountType, accountOptions, persist);
+ }
+
+ private void setSeedAccountDataNoChecks(@UserIdInt int userId, String accountName,
+ String accountType, PersistableBundle accountOptions, boolean persist) {
synchronized (mPackagesLock) {
final UserData userData;
synchronized (mUsersLock) {
@@ -4996,14 +5037,18 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public boolean someUserHasSeedAccount(String accountName, String accountType)
- throws RemoteException {
- checkManageUsersPermission("Cannot check seed account information");
+ public boolean someUserHasSeedAccount(String accountName, String accountType) {
+ checkManageUsersPermission("check seed account information");
+ return someUserHasSeedAccountNoChecks(accountName, accountType);
+ }
+
+ private boolean someUserHasSeedAccountNoChecks(String accountName, String accountType) {
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
final UserData data = mUsers.valueAt(i);
if (data.info.isInitialized()) continue;
+ if (mRemovingUserIds.get(data.info.id)) continue;
if (data.seedAccountName == null || !data.seedAccountName.equals(accountName)) {
continue;
}
@@ -5017,6 +5062,25 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean someUserHasAccount(String accountName, String accountType) {
+ checkManageOrCreateUsersPermission("check seed account information");
+ return someUserHasAccountNoChecks(accountName, accountType);
+ }
+
+ private boolean someUserHasAccountNoChecks(
+ String accountName, String accountType) {
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ return false;
+ }
+
+ final Account account = new Account(accountName, accountType);
+
+ return Binder.withCleanCallingIdentity(() ->
+ AccountManager.get(mContext).someUserHasAccount(account)
+ || someUserHasSeedAccountNoChecks(accountName, accountType));
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 58204891293c..5371454db43c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -1043,10 +1043,12 @@ public class DexManager {
public long deleteOptimizedFiles(ArtPackageInfo packageInfo) {
long freedBytes = 0;
boolean hadErrors = false;
+ final String packageName = packageInfo.getPackageName();
for (String codePath : packageInfo.getCodePaths()) {
for (String isa : packageInfo.getInstructionSets()) {
try {
- freedBytes += mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
+ freedBytes += mInstaller.deleteOdex(packageName, codePath, isa,
+ packageInfo.getOatDir());
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
hadErrors = true;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index ece0a62b7877..e207ff1d9092 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -702,6 +702,14 @@ final class DefaultPermissionGrantPolicy {
DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
userId, CONTACTS_PERMISSIONS);
+ // Maps
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ grantPermissionsToSystemPackage(pm,
+ getDefaultSystemHandlerActivityPackageForCategory(pm,
+ Intent.CATEGORY_APP_MAPS, userId),
+ userId, FOREGROUND_LOCATION_PERMISSIONS);
+ }
+
// Email
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackageForCategory(pm,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2369c5e984f2..131e5873a5a2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -332,6 +332,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -1115,21 +1119,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Global Actions");
showGlobalActions();
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Shut Off");
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
break;
case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Long Press - Go To Voice Assist");
// Some devices allow the voice assistant intent during setup (and use that intent
// to launch something else, like Settings). So we explicitly allow that via the
@@ -1153,7 +1157,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power - Very Long Press - Show Global Actions");
showGlobalActions();
break;
@@ -2098,7 +2102,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerKeyHandled = true;
break;
case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
"Power + Volume Up - Global Actions");
showGlobalActions();
mPowerKeyHandled = true;
@@ -5291,7 +5296,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return false;
}
- mVibrator.vibrate(uid, packageName, effect, reason, TOUCH_VIBRATION_ATTRIBUTES);
+ mVibrator.vibrate(uid, packageName, effect, reason, getVibrationAttributes(effectId));
return true;
}
@@ -5320,6 +5325,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case HapticFeedbackConstants.GESTURE_START:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
case HapticFeedbackConstants.EDGE_SQUEEZE:
return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
@@ -5358,6 +5364,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ private VibrationAttributes getVibrationAttributes(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.ROTARY_SCROLL_TICK:
+ case HapticFeedbackConstants.ROTARY_SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.ROTARY_SCROLL_LIMIT:
+ return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+ default:
+ return TOUCH_VIBRATION_ATTRIBUTES;
+ }
+ }
+
@Override
public void keepScreenOnStartedLw() {
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 6d0f08de64de..70a804b8135b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -110,8 +110,8 @@ public class Notifier {
private static final VibrationEffect CHARGING_VIBRATION_EFFECT =
VibrationEffect.createWaveform(CHARGING_VIBRATION_TIME, CHARGING_VIBRATION_AMPLITUDE,
-1);
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
private final Object mLock = new Object();
@@ -807,7 +807,7 @@ public class Notifier {
final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrate) {
- mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, TOUCH_VIBRATION_ATTRIBUTES);
+ mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
// play sound
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e9d5ad6f6d37..e69acc3071fb 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -470,14 +470,16 @@ public final class TvInputManagerService extends SystemService {
}
private void stopUser(int userId) {
- if (userId == mCurrentUserId) {
- switchUser(ActivityManager.getCurrentUser());
- return;
- }
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ switchUser(ActivityManager.getCurrentUser());
+ return;
+ }
- releaseSessionOfUserLocked(userId);
- unbindServiceOfUserLocked(userId);
- mRunningProfiles.remove(userId);
+ releaseSessionOfUserLocked(userId);
+ unbindServiceOfUserLocked(userId);
+ mRunningProfiles.remove(userId);
+ }
}
private void startProfileLocked(int userId) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 4d1ff9eb3f81..dfb07523d830 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -27,6 +27,7 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManager;
import android.media.tv.interactive.ITvIAppManagerCallback;
@@ -42,6 +43,7 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -58,10 +60,12 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
/**
* This class provides a system service that manages interactive TV applications.
*/
@@ -81,6 +85,8 @@ public class TvIAppManagerService extends SystemService {
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ private final UserManager mUserManager;
+
/**
* Initializes the system service.
* <p>
@@ -93,6 +99,7 @@ public class TvIAppManagerService extends SystemService {
public TvIAppManagerService(Context context) {
super(context);
mContext = context;
+ mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
}
@GuardedBy("mLock")
@@ -330,11 +337,188 @@ public class TvIAppManagerService extends SystemService {
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // TODO: handle switch / start / stop user
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ startUser(userId);
+ } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ stopUser(userId);
+ }
}
}, UserHandle.ALL, intentFilter, null, null);
}
+ private void switchUser(int userId) {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ return;
+ }
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (userInfo.isProfile()) {
+ Slog.w(TAG, "cannot switch to a profile!");
+ return;
+ }
+
+ for (int runningId : mRunningProfiles) {
+ releaseSessionOfUserLocked(runningId);
+ unbindServiceOfUserLocked(runningId);
+ }
+ mRunningProfiles.clear();
+ releaseSessionOfUserLocked(mCurrentUserId);
+ unbindServiceOfUserLocked(mCurrentUserId);
+
+ mCurrentUserId = userId;
+ buildTvIAppServiceListLocked(userId, null);
+ }
+ }
+
+ private void removeUser(int userId) {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ // Release all created sessions.
+ for (SessionState state : userState.mSessionStateMap.values()) {
+ if (state.mSession != null) {
+ try {
+ state.mSession.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ }
+ }
+ userState.mSessionStateMap.clear();
+
+ // Unregister all callbacks and unbind all services.
+ for (ServiceState serviceState : userState.mServiceStateMap.values()) {
+ if (serviceState.mService != null) {
+ if (serviceState.mCallback != null) {
+ try {
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.mConnection);
+ }
+ }
+ userState.mServiceStateMap.clear();
+
+ // Clear everything else.
+ userState.mIAppMap.clear();
+ userState.mPackageSet.clear();
+ userState.mClientStateMap.clear();
+ userState.mCallbacks.kill();
+
+ mRunningProfiles.remove(userId);
+ mUserStates.remove(userId);
+
+ if (userId == mCurrentUserId) {
+ switchUser(UserHandle.USER_SYSTEM);
+ }
+ }
+ }
+
+ private void startUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
+ // user already started
+ return;
+ }
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ UserInfo parentInfo = mUserManager.getProfileParent(userId);
+ if (userInfo.isProfile()
+ && parentInfo != null
+ && parentInfo.id == mCurrentUserId) {
+ // only the children of the current user can be started in background
+ startProfileLocked(userId);
+ }
+ }
+ }
+
+ private void stopUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ switchUser(ActivityManager.getCurrentUser());
+ return;
+ }
+
+ releaseSessionOfUserLocked(userId);
+ unbindServiceOfUserLocked(userId);
+ mRunningProfiles.remove(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startProfileLocked(int userId) {
+ mRunningProfiles.add(userId);
+ buildTvIAppServiceListLocked(userId, null);
+ }
+
+ @GuardedBy("mLock")
+ private void releaseSessionOfUserLocked(int userId) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ List<SessionState> sessionStatesToRelease = new ArrayList<>();
+ for (SessionState sessionState : userState.mSessionStateMap.values()) {
+ if (sessionState.mSession != null) {
+ sessionStatesToRelease.add(sessionState);
+ }
+ }
+ for (SessionState sessionState : sessionStatesToRelease) {
+ try {
+ sessionState.mSession.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ clearSessionAndNotifyClientLocked(sessionState);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void unbindServiceOfUserLocked(int userId) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState == null) {
+ return;
+ }
+ for (Iterator<ComponentName> it = userState.mServiceStateMap.keySet().iterator();
+ it.hasNext(); ) {
+ ComponentName component = it.next();
+ ServiceState serviceState = userState.mServiceStateMap.get(component);
+ if (serviceState != null && serviceState.mSessionTokens.isEmpty()) {
+ if (serviceState.mCallback != null) {
+ try {
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
+ mContext.unbindService(serviceState.mConnection);
+ it.remove();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void clearSessionAndNotifyClientLocked(SessionState state) {
+ if (state.mClient != null) {
+ try {
+ state.mClient.onSessionReleased(state.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ removeSessionStateLocked(state.mSessionToken, state.mUserId);
+ }
+
private SessionState getSessionState(IBinder sessionToken) {
// TODO: implement user state and get session from it.
return null;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index bc21f1bd2ed3..71a6b2265075 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,6 +16,14 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IUidObserver;
@@ -90,6 +98,8 @@ final class VibrationSettings {
@GuardedBy("mLock")
private int mHapticFeedbackIntensity;
@GuardedBy("mLock")
+ private int mHardwareFeedbackIntensity;
+ @GuardedBy("mLock")
private int mNotificationIntensity;
@GuardedBy("mLock")
private int mRingIntensity;
@@ -232,17 +242,20 @@ final class VibrationSettings {
* @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
*/
public int getDefaultIntensity(int usageHint) {
- if (isAlarm(usageHint)) {
+ if (usageHint == USAGE_ALARM) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
}
synchronized (mLock) {
if (mVibrator != null) {
- if (isRingtone(usageHint)) {
- return mVibrator.getDefaultRingVibrationIntensity();
- } else if (isNotification(usageHint)) {
- return mVibrator.getDefaultNotificationVibrationIntensity();
- } else if (isHapticFeedback(usageHint)) {
- return mVibrator.getDefaultHapticFeedbackIntensity();
+ switch (usageHint) {
+ case USAGE_RINGTONE:
+ return mVibrator.getDefaultRingVibrationIntensity();
+ case USAGE_NOTIFICATION:
+ return mVibrator.getDefaultNotificationVibrationIntensity();
+ case USAGE_TOUCH:
+ case USAGE_HARDWARE_FEEDBACK:
+ case USAGE_PHYSICAL_EMULATION:
+ return mVibrator.getDefaultHapticFeedbackIntensity();
}
}
}
@@ -257,16 +270,20 @@ final class VibrationSettings {
*/
public int getCurrentIntensity(int usageHint) {
synchronized (mLock) {
- if (isRingtone(usageHint)) {
- return mRingIntensity;
- } else if (isNotification(usageHint)) {
- return mNotificationIntensity;
- } else if (isHapticFeedback(usageHint)) {
- return mHapticFeedbackIntensity;
- } else if (isAlarm(usageHint)) {
- return Vibrator.VIBRATION_INTENSITY_HIGH;
- } else {
- return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+ switch (usageHint) {
+ case USAGE_RINGTONE:
+ return mRingIntensity;
+ case USAGE_NOTIFICATION:
+ return mNotificationIntensity;
+ case USAGE_TOUCH:
+ return mHapticFeedbackIntensity;
+ case USAGE_HARDWARE_FEEDBACK:
+ case USAGE_PHYSICAL_EMULATION:
+ return mHardwareFeedbackIntensity;
+ case USAGE_ALARM:
+ return Vibrator.VIBRATION_INTENSITY_HIGH;
+ default:
+ return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
}
@@ -289,7 +306,7 @@ final class VibrationSettings {
* for ringtone usage only. All other usages are allowed independently of ringer mode.
*/
public boolean shouldVibrateForRingerMode(int usageHint) {
- if (!isRingtone(usageHint)) {
+ if (usageHint != USAGE_RINGTONE) {
return true;
}
synchronized (mLock) {
@@ -324,8 +341,10 @@ final class VibrationSettings {
* {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate.
*/
public boolean shouldVibrateForPowerMode(int usageHint) {
- return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint)
- || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+ synchronized (mLock) {
+ return !mLowPowerMode || usageHint == USAGE_RINGTONE || usageHint == USAGE_ALARM
+ || usageHint == USAGE_COMMUNICATION_REQUEST;
+ }
}
/** Return {@code true} if input devices should vibrate instead of this device. */
@@ -338,22 +357,6 @@ final class VibrationSettings {
return mZenMode != Settings.Global.ZEN_MODE_OFF;
}
- private static boolean isNotification(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
- }
-
- private static boolean isRingtone(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_RINGTONE;
- }
-
- private static boolean isHapticFeedback(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_TOUCH;
- }
-
- private static boolean isAlarm(int usageHint) {
- return usageHint == VibrationAttributes.USAGE_ALARM;
- }
-
private static boolean isClassAlarm(int usageHint) {
return (usageHint & VibrationAttributes.USAGE_CLASS_MASK)
== VibrationAttributes.USAGE_CLASS_ALARM;
@@ -365,18 +368,35 @@ final class VibrationSettings {
mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0;
mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
+ getDefaultIntensity(USAGE_TOUCH));
+ mHardwareFeedbackIntensity = getSystemSetting(
+ Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ getHardwareFeedbackIntensityWhenSettingIsMissing(mHapticFeedbackIntensity));
mNotificationIntensity = getSystemSetting(
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
+ getDefaultIntensity(USAGE_NOTIFICATION));
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ getDefaultIntensity(USAGE_RINGTONE));
mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
notifyListeners();
}
+ /**
+ * Return the value to be used for {@link Settings.System#HARDWARE_HAPTIC_FEEDBACK_INTENSITY}
+ * when the value was not set by the user.
+ *
+ * <p>This should adapt the behavior preceding the introduction of this new setting key, which
+ * is to apply {@link Settings.System#HAPTIC_FEEDBACK_INTENSITY} unless it's disabled.
+ */
+ private int getHardwareFeedbackIntensityWhenSettingIsMissing(int hapticFeedbackIntensity) {
+ if (hapticFeedbackIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return getDefaultIntensity(USAGE_HARDWARE_FEEDBACK);
+ }
+ return hapticFeedbackIntensity;
+ }
+
@Override
public String toString() {
return "VibrationSettings{"
@@ -389,18 +409,20 @@ final class VibrationSettings {
+ ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
+ ", mRampStepDuration=" + mRampStepDuration
+ ", mRampDownDuration=" + mRampDownDuration
+ + ", mHardwareHapticFeedbackIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
+ ", mHapticFeedbackIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
+ + intensityToString(getCurrentIntensity(USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_TOUCH))
+ + intensityToString(getDefaultIntensity(USAGE_TOUCH))
+ ", mNotificationIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
+ ", mNotificationDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
+ ", mRingIntensity="
- + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE))
+ + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
+ ", mRingDefaultIntensity="
- + intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE))
+ + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+ '}';
}
@@ -410,15 +432,15 @@ final class VibrationSettings {
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
+ getDefaultIntensity(USAGE_TOUCH));
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
mNotificationIntensity);
proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
+ getDefaultIntensity(USAGE_NOTIFICATION));
proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
mRingIntensity);
proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
- getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ getDefaultIntensity(USAGE_RINGTONE));
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index a3273823c42b..fdd991358154 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -287,6 +287,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
}
// If we waited, the queue may have changed, so let the loop run again.
if (waitTime <= 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Play vibration consuming next step...");
+ }
mStepQueue.consumeNext();
}
Vibration.Status status = mStop ? Vibration.Status.CANCELLED
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 4b7fd904e32d..4a1b95bd4596 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -40,21 +40,22 @@ final class VibratorController {
private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
private final Object mLock = new Object();
- private final NativeWrapper mNativeWrapper;
@GuardedBy("mLock")
- private VibratorInfo mVibratorInfo;
- @GuardedBy("mLock")
- private boolean mVibratorInfoLoadSuccessful;
- @GuardedBy("mLock")
+ private final NativeWrapper mNativeWrapper;
+
+ // Vibrator state listeners that support concurrent updates and broadcasts, but should lock
+ // while broadcasting to guarantee delivery order.
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
new RemoteCallbackList<>();
- @GuardedBy("mLock")
- private boolean mIsVibrating;
- @GuardedBy("mLock")
- private boolean mIsUnderExternalControl;
- @GuardedBy("mLock")
- private float mCurrentAmplitude;
+
+ // Vibrator state variables that are updated from synchronized blocks but can be read anytime
+ // for a snippet of the current known vibrator state/info.
+ private volatile VibratorInfo mVibratorInfo;
+ private volatile boolean mVibratorInfoLoadSuccessful;
+ private volatile boolean mIsVibrating;
+ private volatile boolean mIsUnderExternalControl;
+ private volatile float mCurrentAmplitude;
/** Listener for vibration completion callbacks from native. */
public interface OnVibrationCompleteListener {
@@ -86,35 +87,39 @@ final class VibratorController {
/** Register state listener for this vibrator. */
public boolean registerVibratorStateListener(IVibratorStateListener listener) {
- synchronized (mLock) {
- final long token = Binder.clearCallingIdentity();
- try {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Register the listener and send the first state atomically, to avoid potentially
+ // out of order broadcasts in between.
+ synchronized (mLock) {
if (!mVibratorStateListeners.register(listener)) {
return false;
}
// Notify its callback after new client registered.
- notifyStateListenerLocked(listener);
- return true;
- } finally {
- Binder.restoreCallingIdentity(token);
+ notifyStateListener(listener, mIsVibrating);
}
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
/** Remove registered state listener for this vibrator. */
public boolean unregisterVibratorStateListener(IVibratorStateListener listener) {
- synchronized (mLock) {
- final long token = Binder.clearCallingIdentity();
- try {
- return mVibratorStateListeners.unregister(listener);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mVibratorStateListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
/** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
public void reloadVibratorInfoIfNeeded() {
+ // Early check outside lock, for quick return.
+ if (mVibratorInfoLoadSuccessful) {
+ return;
+ }
synchronized (mLock) {
if (mVibratorInfoLoadSuccessful) {
return;
@@ -132,16 +137,12 @@ final class VibratorController {
/** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
boolean isVibratorInfoLoadSuccessful() {
- synchronized (mLock) {
- return mVibratorInfoLoadSuccessful;
- }
+ return mVibratorInfoLoadSuccessful;
}
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
public VibratorInfo getVibratorInfo() {
- synchronized (mLock) {
- return mVibratorInfo;
- }
+ return mVibratorInfo;
}
/**
@@ -151,9 +152,7 @@ final class VibratorController {
* automatically notified to any registered {@link IVibratorStateListener} on change.
*/
public boolean isVibrating() {
- synchronized (mLock) {
- return mIsVibrating;
- }
+ return mIsVibrating;
}
/**
@@ -168,16 +167,12 @@ final class VibratorController {
* <p>If {@link #isVibrating()} is false then this will be zero.
*/
public float getCurrentAmplitude() {
- synchronized (mLock) {
- return mCurrentAmplitude;
- }
+ return mCurrentAmplitude;
}
/** Return {@code true} if this vibrator is under external control, false otherwise. */
public boolean isUnderExternalControl() {
- synchronized (mLock) {
- return mIsUnderExternalControl;
- }
+ return mIsUnderExternalControl;
}
/**
@@ -187,14 +182,14 @@ final class VibratorController {
* @return true if this vibrator has this capability, false otherwise
*/
public boolean hasCapability(long capability) {
- synchronized (mLock) {
- return mVibratorInfo.hasCapability(capability);
- }
+ return mVibratorInfo.hasCapability(capability);
}
/** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
public boolean isAvailable() {
- return mNativeWrapper.isAvailable();
+ synchronized (mLock) {
+ return mNativeWrapper.isAvailable();
+ }
}
/**
@@ -203,10 +198,10 @@ final class VibratorController {
* <p>This will affect the state of {@link #isUnderExternalControl()}.
*/
public void setExternalControl(boolean externalControl) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
+ return;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
- return;
- }
mIsUnderExternalControl = externalControl;
mNativeWrapper.setExternalControl(externalControl);
}
@@ -217,10 +212,10 @@ final class VibratorController {
* if given {@code effect} is {@code null}.
*/
public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
+ return;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
- return;
- }
if (prebaked == null) {
mNativeWrapper.alwaysOnDisable(id);
} else {
@@ -256,7 +251,7 @@ final class VibratorController {
long duration = mNativeWrapper.on(milliseconds, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -277,7 +272,7 @@ final class VibratorController {
prebaked.getEffectStrength(), vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -293,14 +288,14 @@ final class VibratorController {
* do not support the input or a negative number if the operation failed.
*/
public long on(PrimitiveSegment[] primitives, long vibrationId) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ return 0;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
- return 0;
- }
long duration = mNativeWrapper.compose(primitives, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -315,15 +310,15 @@ final class VibratorController {
* @return The duration of the effect playing, or 0 if unsupported.
*/
public long on(RampSegment[] primitives, long vibrationId) {
+ if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
+ return 0;
+ }
synchronized (mLock) {
- if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
- return 0;
- }
int braking = mVibratorInfo.getDefaultBraking();
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
- notifyVibratorOnLocked();
+ notifyListenerOnVibrating(true);
}
return duration;
}
@@ -334,7 +329,7 @@ final class VibratorController {
synchronized (mLock) {
mNativeWrapper.off();
mCurrentAmplitude = 0;
- notifyVibratorOffLocked();
+ notifyListenerOnVibrating(false);
}
}
@@ -349,51 +344,31 @@ final class VibratorController {
@Override
public String toString() {
- synchronized (mLock) {
- return "VibratorController{"
- + "mVibratorInfo=" + mVibratorInfo
- + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
- + ", mIsVibrating=" + mIsVibrating
- + ", mCurrentAmplitude=" + mCurrentAmplitude
- + ", mIsUnderExternalControl=" + mIsUnderExternalControl
- + ", mVibratorStateListeners count="
- + mVibratorStateListeners.getRegisteredCallbackCount()
- + '}';
- }
+ return "VibratorController{"
+ + "mVibratorInfo=" + mVibratorInfo
+ + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
+ + ", mIsVibrating=" + mIsVibrating
+ + ", mCurrentAmplitude=" + mCurrentAmplitude
+ + ", mIsUnderExternalControl=" + mIsUnderExternalControl
+ + ", mVibratorStateListeners count="
+ + mVibratorStateListeners.getRegisteredCallbackCount()
+ + '}';
}
@GuardedBy("mLock")
- private void notifyVibratorOnLocked() {
- if (!mIsVibrating) {
- mIsVibrating = true;
- notifyStateListenersLocked();
+ private void notifyListenerOnVibrating(boolean isVibrating) {
+ if (mIsVibrating != isVibrating) {
+ mIsVibrating = isVibrating;
+ // The broadcast method is safe w.r.t. register/unregister listener methods, but lock
+ // is required here to guarantee delivery order.
+ mVibratorStateListeners.broadcast(
+ listener -> notifyStateListener(listener, isVibrating));
}
}
- @GuardedBy("mLock")
- private void notifyVibratorOffLocked() {
- if (mIsVibrating) {
- mIsVibrating = false;
- notifyStateListenersLocked();
- }
- }
-
- @GuardedBy("mLock")
- private void notifyStateListenersLocked() {
- final int length = mVibratorStateListeners.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- notifyStateListenerLocked(mVibratorStateListeners.getBroadcastItem(i));
- }
- } finally {
- mVibratorStateListeners.finishBroadcast();
- }
- }
-
- @GuardedBy("mLock")
- private void notifyStateListenerLocked(IVibratorStateListener listener) {
+ private void notifyStateListener(IVibratorStateListener listener, boolean isVibrating) {
try {
- listener.onVibrating(mIsVibrating);
+ listener.onVibrating(isVibrating);
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Vibrator state listener failed to call", e);
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 0675ad6451ce..5d40c23610dd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -391,6 +391,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
fillVibrationFallbacks(vib, effect);
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
+ }
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
@@ -498,6 +501,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@VisibleForTesting
void updateServiceState() {
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Updating device state...");
+ }
boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators(
mVibrationSettings.shouldVibrateInputDevices());
@@ -611,6 +617,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
try {
Vibration vib = mCurrentVibration.getVibration();
+ if (DEBUG) {
+ Slog.d(TAG, "Reporting vibration " + vib.id + " finished with status " + status);
+ }
endVibrationLocked(vib, status);
finishAppOpModeLocked(vib.uid, vib.opPkg);
} finally {
@@ -1062,11 +1071,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.d(TAG, "Vibrators released after finished vibration");
}
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Processing vibrators released callback");
+ }
mCurrentVibration = null;
if (mNextVibration != null) {
VibrationThread vibThread = mNextVibration;
mNextVibration = null;
- startVibrationThreadLocked(vibThread);
+ Vibration.Status status = startVibrationThreadLocked(vibThread);
+ if (status != Vibration.Status.RUNNING) {
+ endVibrationLocked(vibThread.getVibration(), status);
+ }
}
}
}
@@ -1248,6 +1263,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
void dumpText(PrintWriter pw) {
pw.println("Vibrator Manager Service:");
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to text...");
+ }
pw.println(" mVibrationSettings:");
pw.println(" " + mVibrationSettings);
pw.println();
@@ -1290,6 +1308,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to proto...");
+ }
mVibrationSettings.dumpProto(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ea22d92c1e20..2a1bb0b87d8b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6224,15 +6224,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
public boolean inputDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
WindowProcessController anrApp;
- boolean windowFromSameProcessAsActivity;
+ boolean blameActivityProcess;
synchronized (mAtmService.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
- windowFromSameProcessAsActivity =
- !hasProcess() || app.getPid() == windowPid || windowPid == INVALID_PID;
+ blameActivityProcess = hasProcess()
+ && (app.getPid() == windowPid || windowPid == INVALID_PID);
}
- if (windowFromSameProcessAsActivity) {
+ if (blameActivityProcess) {
return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
anrActivity.shortComponentName, anrActivity.info.applicationInfo,
shortComponentName, app, false, reason);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 210b0aebceb2..ecc85878353a 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -496,11 +496,13 @@ public class ActivityStartController {
* @param activityIntent intent to start the activity.
* @param activityOptions ActivityOptions to start the activity with.
* @param resultTo the caller activity
+ * @param callingUid the caller uid
+ * @param callingPid the caller pid
* @return the start result.
*/
int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
@NonNull Intent activityIntent, @Nullable Bundle activityOptions,
- @Nullable IBinder resultTo) {
+ @Nullable IBinder resultTo, int callingUid, int callingPid) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
return obtainStarter(activityIntent, "startActivityInTaskFragment")
@@ -508,8 +510,8 @@ public class ActivityStartController {
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
- .setCallingUid(Binder.getCallingUid())
- .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
.setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId())
.execute();
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index df9a6d21113e..878822522d08 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -590,7 +590,7 @@ public class AppTransitionController {
}
// We don't want the organizer to handle transition of non-embedded activity of other
// app.
- if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
+ if (r.getUid() != task.effectiveUid && !r.isEmbedded()) {
leafTask = null;
break;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 42c81248da34..55acfb668859 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -218,6 +218,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import com.android.internal.annotations.VisibleForTesting;
@@ -696,6 +697,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// well and thus won't change the top resumed / focused record
boolean mDontMoveToTop;
+ /**
+ * The policy controller of the windows that can be displayed on the virtual display.
+ *
+ * @see DisplayWindowPolicyController
+ */
+ @Nullable
+ DisplayWindowPolicyController mDisplayWindowPolicyController;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -2739,6 +2748,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (newDisplayInfo != null) {
mDisplayInfo.copyFrom(newDisplayInfo);
}
+
+ mDisplayWindowPolicyController =
+ displayManagerInternal.getDisplayWindowPolicyController(mDisplayId);
}
updateBaseDisplayMetrics(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight,
@@ -3420,6 +3432,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mInputMonitor.dump(pw, " ");
pw.println();
mInsetsStateController.dump(prefix, pw);
+ if (mDisplayWindowPolicyController != null) {
+ pw.println();
+ mDisplayWindowPolicyController.dump(prefix, pw);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index badb1f5a0a12..963f3265757d 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -139,9 +139,6 @@ class EnsureActivitiesVisibleHelper {
setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
}
}
- if (mTaskFragment.mTransitionController.isShellTransitionsEnabled()) {
- mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
- }
}
private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 3de98f18dae8..7a7fb6570694 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -169,21 +169,35 @@ public class PackageConfigPersister {
}
}
+ /**
+ * Returns true when the app specific configuration is successfully stored or removed based on
+ * the current requested configuration. It will return false when the requested
+ * configuration is same as the pre-existing app-specific configuration.
+ */
@GuardedBy("mLock")
boolean updateFromImpl(String packageName, int userId,
PackageConfigurationUpdaterImpl impl) {
synchronized (mLock) {
- PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
- if (impl.getNightMode() != null) {
- record.mNightMode = impl.getNightMode();
- }
- if (impl.getLocales() != null) {
- record.mLocales = impl.getLocales();
+ boolean isRecordPresent = false;
+ PackageConfigRecord record = findRecord(mModified, packageName, userId);
+ if (record != null) {
+ isRecordPresent = true;
+ } else {
+ record = findRecordOrCreate(mModified, packageName, userId);
}
+ boolean isNightModeChanged = updateNightMode(impl.getNightMode(), record);
+ boolean isLocalesChanged = updateLocales(impl.getLocales(), record);
+
if ((record.mNightMode == null || record.isResetNightMode())
&& (record.mLocales == null || record.mLocales.isEmpty())) {
// if all values default to system settings, we can remove the package.
removePackage(packageName, userId);
+ // if there was a pre-existing record for the package that was deleted,
+ // we return true (since it was successfully deleted), else false (since there was
+ // no change to the previous state).
+ return isRecordPresent;
+ } else if (!isNightModeChanged && !isLocalesChanged) {
+ return false;
} else {
final PackageConfigRecord pendingRecord =
findRecord(mPendingWrite, record.mName, record.mUserId);
@@ -195,7 +209,8 @@ public class PackageConfigPersister {
writeRecord = pendingRecord;
}
- if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
+ if (!updateNightMode(record.mNightMode, writeRecord)
+ && !updateLocales(record.mLocales, writeRecord)) {
return false;
}
@@ -203,24 +218,24 @@ public class PackageConfigPersister {
Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
}
mPersisterQueue.addItem(new WriteProcessItem(writeRecord), false /* flush */);
+ return true;
}
- return true;
}
}
- private boolean updateNightMode(PackageConfigRecord record, PackageConfigRecord writeRecord) {
- if (record.mNightMode == null || record.mNightMode.equals(writeRecord.mNightMode)) {
+ private boolean updateNightMode(Integer requestedNightMode, PackageConfigRecord record) {
+ if (requestedNightMode == null || requestedNightMode.equals(record.mNightMode)) {
return false;
}
- writeRecord.mNightMode = record.mNightMode;
+ record.mNightMode = requestedNightMode;
return true;
}
- private boolean updateLocales(PackageConfigRecord record, PackageConfigRecord writeRecord) {
- if (record.mLocales == null || record.mLocales.equals(writeRecord.mLocales)) {
+ private boolean updateLocales(LocaleList requestedLocaleList, PackageConfigRecord record) {
+ if (requestedLocaleList == null || requestedLocaleList.equals(record.mLocales)) {
return false;
}
- writeRecord.mLocales = record.mLocales;
+ record.mLocales = requestedLocaleList;
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 6d83fb6d12c9..29c27f9f3af6 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -31,10 +31,8 @@ import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
-import android.window.TaskFragmentAppearedInfo;
import android.window.TaskFragmentInfo;
import com.android.internal.protolog.common.ProtoLog;
@@ -135,11 +133,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
- "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
try {
- organizer.onTaskFragmentAppeared(
- new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ organizer.onTaskFragmentAppeared(info);
mLastSentTaskFragmentInfos.put(tf, info);
tf.mTaskFragmentAppearedSent = true;
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f175eec15064..7349594483a8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -244,11 +244,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mParticipants.add(wc);
if (info.mShowWallpaper) {
- // Collect the wallpaper so it is part of the sync set.
- final WindowContainer wallpaper =
+ // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
+ final WindowState wallpaper =
wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
if (wallpaper != null) {
- collect(wallpaper);
+ collect(wallpaper.mToken);
}
}
}
@@ -495,25 +495,35 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
return;
}
- int displayId = DEFAULT_DISPLAY;
- for (WindowContainer container : mParticipants) {
- if (container.mDisplayContent == null) continue;
- displayId = container.mDisplayContent.getDisplayId();
+ boolean hasWallpaper = false;
+ DisplayContent dc = null;
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = mParticipants.valueAt(i);
+ if (dc == null && wc.mDisplayContent != null) {
+ dc = wc.mDisplayContent;
+ }
+ if (!hasWallpaper && isWallpaper(wc)) {
+ hasWallpaper = true;
+ }
}
+ if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
if (mState == STATE_ABORT) {
mController.abort(this);
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
- .getPendingTransaction().merge(transaction);
+ dc.getPendingTransaction().merge(transaction);
mSyncId = -1;
mOverrideOptions = null;
return;
}
+ // Ensure that wallpaper visibility is updated with the latest wallpaper target.
+ if (hasWallpaper) {
+ dc.mWallpaperController.adjustWallpaperWindows();
+ }
mState = STATE_PLAYING;
mController.moveToPlaying(this);
- if (mController.mAtm.mTaskSupervisor.getKeyguardController().isKeyguardLocked(displayId)) {
+ if (dc.isKeyguardLocked()) {
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
@@ -523,9 +533,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
info.setAnimationOptions(mOverrideOptions);
// TODO(b/188669821): Move to animation impl in shell.
- handleLegacyRecentsStartBehavior(displayId, info);
+ handleLegacyRecentsStartBehavior(dc, info);
- handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ handleNonAppWindowsInTransition(dc, mType, mFlags);
reportStartReasonsToLogger();
@@ -627,14 +637,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/** @see RecentsAnimationController#attachNavigationBarToApp */
- private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+ private void handleLegacyRecentsStartBehavior(DisplayContent dc, TransitionInfo info) {
if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
return;
}
- final DisplayContent dc =
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
- if (dc == null) return;
- mRecentsDisplayId = displayId;
+ mRecentsDisplayId = dc.mDisplayId;
// Recents has an input-consumer to grab input from the "live tile" app. Set that up here
final InputConsumerImpl recentsAnimationInputConsumer =
@@ -679,7 +686,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Find the top-most non-home, closing app.
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change c = info.getChanges().get(i);
- if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+ if (c.getTaskInfo() == null || c.getTaskInfo().displayId != mRecentsDisplayId
|| c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
|| !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
continue;
@@ -710,7 +717,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
}
if (mController.mStatusBar != null) {
- mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, false);
}
}
@@ -760,13 +767,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
- private void handleNonAppWindowsInTransition(int displayId,
+ private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
@TransitionType int transit, @TransitionFlags int flags) {
- final DisplayContent dc =
- mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
- if (dc == null) {
- return;
- }
if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
|| (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
&& !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 08b1a2f39953..e24be378d29a 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -611,8 +611,9 @@ class WallpaperController {
private void updateWallpaperTokens(boolean visible) {
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.updateWallpaperWindows(visible);
- token.getDisplayContent().assignWindowLayers(false);
+ if (token.updateWallpaperWindows(visible)) {
+ token.mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 3a639f50603f..fe405e5b3af8 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -104,18 +104,21 @@ class WallpaperWindowToken extends WindowToken {
}
}
- void updateWallpaperWindows(boolean visible) {
+ /** Returns {@code true} if visibility is changed. */
+ boolean updateWallpaperWindows(boolean visible) {
+ boolean changed = false;
if (isVisible() != visible) {
ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
token, visible);
setVisibility(visible);
+ changed = true;
}
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
if (mTransitionController.isShellTransitionsEnabled()) {
- return;
+ return changed;
}
- final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+ final WindowState wallpaperTarget =
+ mDisplayContent.mWallpaperController.getWallpaperTarget();
if (visible && wallpaperTarget != null) {
final RecentsAnimationController recentsAnimationController =
@@ -137,6 +140,7 @@ class WallpaperWindowToken extends WindowToken {
}
setVisible(visible);
+ return changed;
}
private void setVisible(boolean visible) {
@@ -155,10 +159,12 @@ class WallpaperWindowToken extends WindowToken {
* transition. In that situation, make sure to call {@link #commitVisibility} when done.
*/
void setVisibility(boolean visible) {
- // Before setting mVisibleRequested so we can track changes.
- mTransitionController.collect(this);
+ if (mVisibleRequested != visible) {
+ // Before setting mVisibleRequested so we can track changes.
+ mTransitionController.collect(this);
- setVisibleRequested(visible);
+ setVisibleRequested(visible);
+ }
// If in a transition, defer commits for activities that are going invisible
if (!visible && (mTransitionController.inTransition()
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 3d479d1e0d68..0649b25e0f04 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -587,7 +587,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
final TaskFragmentCreationParams taskFragmentCreationOptions =
hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken, caller);
break;
}
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -630,7 +630,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
final int result = mService.getActivityStartController()
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
- hop.getCallingActivity());
+ hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
errorCallbackToken,
@@ -1199,7 +1199,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
final ActivityRecord ownerActivity =
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
@@ -1217,9 +1217,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
- // The ownerActivity has to belong to the same app as the root Activity of the target Task.
- final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
- if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // The ownerActivity has to belong to the same app as the target Task.
+ if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
+ || ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 0a55003e77fc..e4c8871e6f8d 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -29,6 +29,8 @@
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/BnGnss.h>
#include <android/hardware/gnss/BnGnssCallback.h>
+#include <android/hardware/gnss/BnGnssGeofence.h>
+#include <android/hardware/gnss/BnGnssGeofenceCallback.h>
#include <android/hardware/gnss/BnGnssMeasurementCallback.h>
#include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
#include <android/hardware/gnss/BnGnssPsdsCallback.h>
@@ -191,6 +193,9 @@ using android::hardware::gnss::IGnssPowerIndicationCallback;
using android::hardware::gnss::PsdsType;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
+using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
+using IGnssGeofenceAidl = android::hardware::gnss::IGnssGeofence;
+using IGnssGeofenceCallbackAidl = android::hardware::gnss::IGnssGeofenceCallback;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
@@ -216,6 +221,8 @@ sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnssAidl> gnssHalAidl = nullptr;
+sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
+sp<IGnssGeofenceAidl> gnssGeofenceAidlIface = nullptr;
sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
@@ -709,35 +716,25 @@ Return<void> GnssXtraCallback::downloadRequestCb() {
return Void();
}
-/*
- * GnssGeofenceCallback class implements the callback methods for the
- * IGnssGeofence interface.
- */
-struct GnssGeofenceCallback : public IGnssGeofenceCallback {
- // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
- Return<void> gnssGeofenceTransitionCb(
- int32_t geofenceId,
- const GnssLocation_V1_0& location,
- GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) override;
- Return<void>
- gnssGeofenceStatusCb(
- GeofenceAvailability status,
- const GnssLocation_V1_0& location) override;
- Return<void> gnssGeofenceAddCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofencePauseCb(int32_t geofenceId,
- GeofenceStatus status) override;
- Return<void> gnssGeofenceResumeCb(int32_t geofenceId,
- GeofenceStatus status) override;
+/** Util class for GnssGeofenceCallback methods. */
+struct GnssGeofenceCallbackUtil {
+ template <class T>
+ static void gnssGeofenceTransitionCb(int geofenceId, const T& location, int transition,
+ int64_t timestampMillis);
+ template <class T>
+ static void gnssGeofenceStatusCb(int availability, const T& lastLocation);
+ static void gnssGeofenceAddCb(int geofenceId, int status);
+ static void gnssGeofenceRemoveCb(int geofenceId, int status);
+ static void gnssGeofencePauseCb(int geofenceId, int status);
+ static void gnssGeofenceResumeCb(int geofenceId, int status);
+
+private:
+ GnssGeofenceCallbackUtil() = delete;
};
-Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
- int32_t geofenceId, const GnssLocation_V1_0& location,
- GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) {
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(int geofenceId, const T& location,
+ int transition, int64_t timestamp) {
JNIEnv* env = getJniEnv();
jobject jLocation = translateGnssLocation(env, location);
@@ -751,27 +748,22 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
- return Void();
}
-Return<void>
-GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability status,
- const GnssLocation_V1_0& location) {
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(int availability, const T& lastLocation) {
JNIEnv* env = getJniEnv();
- jobject jLocation = translateGnssLocation(env, location);
+ jobject jLocation = translateGnssLocation(env, lastLocation);
- env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
- jLocation);
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, availability, jLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(jLocation);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceAddCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
}
@@ -780,13 +772,11 @@ Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
geofenceId,
status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
}
@@ -794,13 +784,11 @@ Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
method_reportGeofenceRemoveStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofencePauseCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
}
@@ -808,13 +796,11 @@ Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
method_reportGeofencePauseStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
}
-Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
- GeofenceStatus status) {
+void GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(int geofenceId, int status) {
JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
+ if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
}
@@ -822,6 +808,104 @@ Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
method_reportGeofenceResumeStatus,
geofenceId, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+/*
+ * GnssGeofenceCallbackAidl class implements the callback methods for the IGnssGeofence AIDL
+ * interface.
+ */
+struct GnssGeofenceCallbackAidl : public android::hardware::gnss::BnGnssGeofenceCallback {
+ Status gnssGeofenceTransitionCb(int geofenceId, const GnssLocationAidl& location,
+ int transition, int64_t timestampMillis) override;
+ Status gnssGeofenceStatusCb(int availability, const GnssLocationAidl& lastLocation) override;
+ Status gnssGeofenceAddCb(int geofenceId, int status) override;
+ Status gnssGeofenceRemoveCb(int geofenceId, int status) override;
+ Status gnssGeofencePauseCb(int geofenceId, int status) override;
+ Status gnssGeofenceResumeCb(int geofenceId, int status) override;
+};
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceTransitionCb(int geofenceId,
+ const GnssLocationAidl& location,
+ int transition, int64_t timestampMillis) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, transition,
+ timestampMillis);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceStatusCb(int availability,
+ const GnssLocationAidl& lastLocation) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(availability, lastLocation);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceAddCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceRemoveCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofencePauseCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceResumeCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, status);
+ return Status::ok();
+}
+
+/*
+ * GnssGeofenceCallback class implements the callback methods for the
+ * IGnssGeofence HIDL interface.
+ */
+struct GnssGeofenceCallback : public IGnssGeofenceCallback {
+ // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
+ Return<void> gnssGeofenceTransitionCb(int32_t geofenceId, const GnssLocation_V1_0& location,
+ GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) override;
+ Return<void> gnssGeofenceStatusCb(GeofenceAvailability status,
+ const GnssLocation_V1_0& location) override;
+ Return<void> gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) override;
+ Return<void> gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) override;
+};
+
+Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
+ int32_t geofenceId, const GnssLocation_V1_0& location, GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, (int)transition,
+ (int64_t)timestamp);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability availability,
+ const GnssLocation_V1_0& location) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb((int)availability, location);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, (int)status);
return Void();
}
@@ -1279,11 +1363,13 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
android_location_gnss_hal_GnssNative_set_gps_service_handle();
}
- if (gnssHal == nullptr) {
+ if (gnssHal == nullptr && gnssHalAidl == nullptr) {
ALOGE("Unable to get GPS service\n");
return;
}
+ // TODO: linkToDeath for AIDL HAL
+
gnssHalDeathRecipient = new GnssDeathRecipient();
hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
if (!linked.isOk()) {
@@ -1303,7 +1389,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
} else {
ALOGD("Unable to get a handle to PSDS AIDL interface.");
}
- } else {
+ } else if (gnssHal != nullptr) {
auto gnssXtra = gnssHal->getExtensionXtra();
if (!gnssXtra.isOk()) {
ALOGD("Unable to get a handle to Xtra");
@@ -1320,7 +1406,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
agnssRilIface_V2_0 = agnssRil_V2_0;
agnssRilIface = agnssRilIface_V2_0;
}
- } else {
+ } else if (gnssHal != nullptr) {
auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
if (!agnssRil_V1_0.isOk()) {
ALOGD("Unable to get a handle to AGnssRil");
@@ -1336,7 +1422,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
} else {
agnssIface_V2_0 = agnss_V2_0;
}
- } else {
+ } else if (gnssHal != nullptr) {
auto agnss_V1_0 = gnssHal->getExtensionAGnss();
if (!agnss_V1_0.isOk()) {
ALOGD("Unable to get a handle to AGnss");
@@ -1345,11 +1431,13 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
}
- auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
- if (!gnssNavigationMessage.isOk()) {
- ALOGD("Unable to get a handle to GnssNavigationMessage");
- } else {
- gnssNavigationMessageIface = gnssNavigationMessage;
+ if (gnssHal != nullptr) {
+ auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+ if (!gnssNavigationMessage.isOk()) {
+ ALOGD("Unable to get a handle to GnssNavigationMessage");
+ } else {
+ gnssNavigationMessageIface = gnssNavigationMessage;
+ }
}
// Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
@@ -1387,12 +1475,12 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
}
}
- if (gnssMeasurementIface == nullptr) {
- auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
- if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
- gnssMeasurementIface =
- std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
- }
+ if (gnssHal != nullptr && gnssMeasurementIface == nullptr) {
+ auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
+ if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
+ gnssMeasurementIface =
+ std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
+ }
}
if (gnssHal_V2_1 != nullptr) {
@@ -1434,7 +1522,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
gnssDebugIface = gnssDebugIface_V2_0;
}
}
- if (gnssDebugIface == nullptr) {
+ if (gnssHal != nullptr && gnssDebugIface == nullptr) {
auto gnssDebug = gnssHal->getExtensionGnssDebug();
if (!gnssDebug.isOk()) {
ALOGD("Unable to get a handle to GnssDebug");
@@ -1443,11 +1531,13 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
}
- auto gnssNi = gnssHal->getExtensionGnssNi();
- if (!gnssNi.isOk()) {
- ALOGD("Unable to get a handle to GnssNi");
- } else {
- gnssNiIface = gnssNi;
+ if (gnssHal != nullptr) {
+ auto gnssNi = gnssHal->getExtensionGnssNi();
+ if (!gnssNi.isOk()) {
+ ALOGD("Unable to get a handle to GnssNi");
+ } else {
+ gnssNiIface = gnssNi;
+ }
}
if (gnssHalAidl != nullptr) {
@@ -1488,11 +1578,17 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
}
- auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
- if (!gnssGeofencing.isOk()) {
- ALOGD("Unable to get a handle to GnssGeofencing");
- } else {
- gnssGeofencingIface = gnssGeofencing;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IGnssGeofenceAidl> gnssGeofenceAidl;
+ auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofenceAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence interface.")) {
+ gnssGeofenceAidlIface = gnssGeofenceAidl;
+ }
+ } else if (gnssHal != nullptr) {
+ auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+ if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
+ gnssGeofencingIface = gnssGeofencing;
+ }
}
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
@@ -1507,7 +1603,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
gnssBatchingIface = std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
}
}
- if (gnssBatchingIface == nullptr) {
+ if (gnssHal != nullptr && gnssBatchingIface == nullptr) {
auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
@@ -1568,7 +1664,7 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
/*
* Fail if the main interface fails to initialize
*/
- if (gnssHal == nullptr) {
+ if (gnssHal == nullptr && gnssHalAidl == nullptr) {
ALOGE("Unable to initialize GNSS HAL.");
return JNI_FALSE;
}
@@ -1583,7 +1679,7 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
} else if (gnssHal_V1_1 != nullptr) {
result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
- } else {
+ } else if (gnssHal != nullptr) {
result = gnssHal->setCallback(gnssCbIface);
}
@@ -1630,10 +1726,18 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
}
// Set IGnssGeofencing.hal callback.
- sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
- if (gnssGeofencingIface != nullptr) {
+ if (gnssGeofenceAidlIface != nullptr) {
+ sp<IGnssGeofenceCallbackAidl> gnssGeofenceCallbackAidl = new GnssGeofenceCallbackAidl();
+ auto status = gnssGeofenceAidlIface->setCallback(gnssGeofenceCallbackAidl);
+ if (!checkAidlStatus(status, "IGnssGeofenceAidl setCallback() failed.")) {
+ gnssGeofenceAidlIface = nullptr;
+ }
+ } else if (gnssGeofencingIface != nullptr) {
+ sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
- checkHidlReturn(status, "IGnssGeofencing setCallback() failed.");
+ if (!checkHidlReturn(status, "IGnssGeofencing setCallback() failed.")) {
+ gnssGeofencingIface = nullptr;
+ }
} else {
ALOGI("Unable to initialize IGnssGeofencing interface.");
}
@@ -1693,12 +1797,15 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
}
static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) {
- if (gnssHal == nullptr) {
- return;
+ if (gnssHalAidl != nullptr) {
+ auto status = gnssHalAidl->close();
+ checkAidlStatus(status, "IGnssAidl close() failed.");
}
- auto result = gnssHal->cleanup();
- checkHidlReturn(result, "IGnss cleanup() failed.");
+ if (gnssHal != nullptr) {
+ auto result = gnssHal->cleanup();
+ checkHidlReturn(result, "IGnss cleanup() failed.");
+ }
}
static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
@@ -2177,57 +2284,85 @@ static void android_location_GnssNetworkConnectivityHandler_update_network_state
static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
jclass) {
- return (gnssGeofencingIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ if (gnssGeofencingIface == nullptr && gnssGeofenceAidlIface == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jboolean android_location_gnss_hal_GnssNative_add_geofence(
JNIEnv* /* env */, jclass, jint geofenceId, jdouble latitude, jdouble longitude,
jdouble radius, jint last_transition, jint monitor_transition,
jint notification_responsiveness, jint unknown_timer) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status =
+ gnssGeofenceAidlIface->addGeofence(geofenceId, latitude, longitude, radius,
+ last_transition, monitor_transition,
+ notification_responsiveness, unknown_timer);
+ return checkAidlStatus(status, "IGnssGeofenceAidl addGeofence() failed.");
}
- auto result = gnssGeofencingIface->addGeofence(
- geofenceId, latitude, longitude, radius,
- static_cast<IGnssGeofenceCallback::GeofenceTransition>(last_transition),
- monitor_transition, notification_responsiveness, unknown_timer);
- return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface
+ ->addGeofence(geofenceId, latitude, longitude, radius,
+ static_cast<IGnssGeofenceCallback::GeofenceTransition>(
+ last_transition),
+ monitor_transition, notification_responsiveness,
+ unknown_timer);
+ return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_remove_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->removeGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl removeGeofence() failed.");
+ }
+
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->removeGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
}
- auto result = gnssGeofencingIface->removeGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_pause_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->pauseGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl pauseGeofence() failed.");
}
- auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_resume_geofence(JNIEnv* /* env */, jclass,
jint geofenceId,
jint monitor_transition) {
- if (gnssGeofencingIface == nullptr) {
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ if (gnssGeofenceAidlIface != nullptr) {
+ auto status = gnssGeofenceAidlIface->resumeGeofence(geofenceId, monitor_transition);
+ return checkAidlStatus(status, "IGnssGeofenceAidl resumeGeofence() failed.");
}
- auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
- return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
+ if (gnssGeofencingIface != nullptr) {
+ auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
+ return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
+ }
+
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
static jboolean android_location_gnss_hal_GnssNative_is_antenna_info_supported(JNIEnv* env,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 727f26573fac..3839a9f39158 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8460,7 +8460,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean hasDeviceOwner() {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller));
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller)
+ || canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mOwners.hasDeviceOwner();
}
@@ -8640,7 +8642,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
@@ -8986,7 +8989,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
+ Preconditions.checkCallAuthorization(canManageUsers(caller)
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return getUserProvisioningState(caller.getUserId());
}
@@ -9240,7 +9244,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return getProfileOwnerNameUnchecked(userHandle);
}
@@ -16466,7 +16471,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
long id = mInjector.binderClearCallingIdentity();
try {
@@ -16492,7 +16498,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked());
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9119133add6a..2f9e3344b29c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -373,6 +373,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.blob.BlobStoreManagerService";
private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.appsearch.AppSearchManagerService";
+ private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
+ "com.android.server.compos.IsolatedCompilationService";
private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
"com.android.server.rollback.RollbackManagerService";
private static final String ALARM_MANAGER_SERVICE_CLASS =
@@ -2718,6 +2720,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) {
+ t.traceBegin("IsolatedCompilationService");
+ mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
t.traceBegin("StartMediaCommunicationService");
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index bbfa4610feea..cfc81e684a7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -29,6 +29,7 @@ import com.android.server.apphibernation.AppHibernationManagerInternal
import com.android.server.apphibernation.AppHibernationService
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.testutils.whenever
+import org.junit.Assert
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -36,7 +37,6 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -102,9 +102,10 @@ class PackageManagerServiceHibernationTests {
val pm = createPackageManagerService()
rule.system().validateFinalState()
- pm.setPackageStoppedState(TEST_PACKAGE_NAME, true, TEST_USER_ID)
TestableLooper.get(this).processAllMessages()
- Mockito.clearInvocations(appHibernationManager)
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
@@ -115,6 +116,31 @@ class PackageManagerServiceHibernationTests {
}
@Test
+ fun testExitForceStop_nonExistingAppHibernationManager_doesNotThrowException() {
+ whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
+ .thenReturn(null)
+
+ rule.system().stageScanExistingPackage(
+ TEST_PACKAGE_NAME,
+ 1L,
+ rule.system().dataAppDirectory)
+ val pm = createPackageManagerService()
+ rule.system().validateFinalState()
+
+ TestableLooper.get(this).processAllMessages()
+
+ whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(true)
+
+ try {
+ pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+ TestableLooper.get(this).processAllMessages()
+ } catch (e: Exception) {
+ Assert.fail("Method throws exception when AppHibernationManager is not ready.\n$e")
+ }
+ }
+
+ @Test
fun testGetOptimizablePackages_ExcludesGloballyHibernatingPackages() {
rule.system().stageScanExistingPackage(
TEST_PACKAGE_NAME,
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ba580ecaad16..e3c60fdfc697 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -108,6 +108,7 @@ android_test {
data: [
":JobTestApp",
+ ":StubTestApp",
],
java_resources: [
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 5a0f1ee963a2..4c638d669019 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -28,6 +28,17 @@
<option name="test-file-name" value="SimpleServiceTestApp3.apk" />
</target_preparer>
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/servicestests" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/servicestests"/>
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/>
+ </target_preparer>
+
<option name="test-tag" value="FrameworksServicesTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 086e3c0901a8..a83d51b0e5e7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -68,6 +68,7 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.MagnificationConfig;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -650,8 +651,12 @@ public class AbstractAccessibilityServiceConnectionTest {
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationProcessor.setScaleAndCenter(displayId,
- scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
+ MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
+ SERVICE_ID)).thenReturn(true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.setMagnificationScaleAndCenter(
@@ -665,8 +670,12 @@ public class AbstractAccessibilityServiceConnectionTest {
final float scale = 1.8f;
final float centerX = 50.5f;
final float centerY = 100.5f;
- when(mMockMagnificationProcessor.setScaleAndCenter(displayId,
- scale, centerX, centerY, true, SERVICE_ID)).thenReturn(true);
+ MagnificationConfig config = new MagnificationConfig.Builder()
+ .setScale(scale)
+ .setCenterX(centerX)
+ .setCenterY(centerY).build();
+ when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
+ SERVICE_ID)).thenReturn(true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.setMagnificationScaleAndCenter(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index c412b9403561..2fd28167df16 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -16,25 +16,32 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
+import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.MagnificationConfig;
import android.graphics.Region;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
+import com.android.server.accessibility.magnification.WindowMagnificationManager;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-
+import org.mockito.stubbing.Answer;
/**
* Tests for the {@link MagnificationProcessor}
@@ -42,57 +49,115 @@ import org.mockito.MockitoAnnotations;
public class MagnificationProcessorTest {
private static final int TEST_DISPLAY = 0;
-
+ private static final int SERVICE_ID = 42;
+ private static final float TEST_SCALE = 1.8f;
+ private static final float TEST_CENTER_X = 50.5f;
+ private static final float TEST_CENTER_Y = 100.5f;
private MagnificationProcessor mMagnificationProcessor;
@Mock
private MagnificationController mMockMagnificationController;
@Mock
private FullScreenMagnificationController mMockFullScreenMagnificationController;
+ @Mock
+ private WindowMagnificationManager mMockWindowMagnificationManager;
+ FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
+ WindowMagnificationManagerStub mWindowMagnificationManagerStub;
@Before
+
public void setup() {
MockitoAnnotations.initMocks(this);
-
+ mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
+ mMockFullScreenMagnificationController);
+ mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
+ mMockWindowMagnificationManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
+ when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
+ mMockWindowMagnificationManager);
mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
}
@Test
- public void getScale() {
- final float result = 2;
- when(mMockFullScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(result);
+ public void getScale_fullscreenMode_expectedValue() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
+
+ assertEquals(scale, TEST_SCALE, 0);
+ }
+
+ @Test
+ public void getScale_windowMode_expectedValue() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
- assertEquals(scale, result, 0);
+ assertEquals(scale, TEST_SCALE, 0);
}
@Test
- public void getCenterX_canControlMagnification_returnCenterX() {
- final float result = 200;
- when(mMockFullScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(result);
+ public void getCenterX_canControlFullscreenMagnification_returnCenterX() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float centerX = mMagnificationProcessor.getCenterX(
TEST_DISPLAY, /* canControlMagnification= */true);
- assertEquals(centerX, result, 0);
+ assertEquals(centerX, TEST_CENTER_X, 0);
}
@Test
- public void getCenterY_canControlMagnification_returnCenterY() {
- final float result = 300;
- when(mMockFullScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(result);
+ public void getCenterX_canControlWindowMagnification_returnCenterX() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float centerX = mMagnificationProcessor.getCenterX(
+ TEST_DISPLAY, /* canControlMagnification= */true);
+
+ assertEquals(centerX, TEST_CENTER_X, 0);
+ }
+
+ @Test
+ public void getCenterY_canControlFullscreenMagnification_returnCenterY() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
float centerY = mMagnificationProcessor.getCenterY(
TEST_DISPLAY, /* canControlMagnification= */false);
- assertEquals(centerY, result, 0);
+ assertEquals(centerY, TEST_CENTER_Y, 0);
}
@Test
- public void getMagnificationRegion_canControlMagnification_returnRegion() {
+ public void getCenterY_canControlWindowMagnification_returnCenterY() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ float centerY = mMagnificationProcessor.getCenterY(
+ TEST_DISPLAY, /* canControlMagnification= */false);
+
+ assertEquals(centerY, TEST_CENTER_Y, 0);
+ }
+
+ @Test
+ public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
@@ -101,14 +166,25 @@ public class MagnificationProcessorTest {
}
@Test
- public void getMagnificationRegion_notRegistered_shouldRegisterThenUnregister() {
+ public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+ mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ region, /* canControlMagnification= */true);
+
+ verify(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ eq(region));
+ }
+
+ @Test
+ public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
}).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
any());
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
final Region result = new Region();
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
@@ -119,44 +195,242 @@ public class MagnificationProcessorTest {
}
@Test
- public void getMagnificationCenterX_notRegistered_shouldRegisterThenUnregister() {
- final float centerX = 480.0f;
- when(mMockFullScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(centerX);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterX(TEST_CENTER_X).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
final float result = mMagnificationProcessor.getCenterX(
TEST_DISPLAY, /* canControlMagnification= */ true);
- assertEquals(centerX, result, 0);
+ assertEquals(TEST_CENTER_X, result, 0);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
verify(mMockFullScreenMagnificationController).unregister(TEST_DISPLAY);
}
@Test
- public void getMagnificationCenterY_notRegistered_shouldRegisterThenUnregister() {
- final float centerY = 640.0f;
- when(mMockFullScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(centerY);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getMagnificationCenterY_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
final float result = mMagnificationProcessor.getCenterY(
TEST_DISPLAY, /* canControlMagnification= */ true);
- assertEquals(centerY, result, 0);
+ assertEquals(TEST_CENTER_Y, result, 0);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
verify(mMockFullScreenMagnificationController).unregister(TEST_DISPLAY);
}
@Test
- public void setMagnificationScaleAndCenter_notRegistered_shouldRegister() {
- final int serviceId = 42;
- final float scale = 1.8f;
- final float centerX = 50.5f;
- final float centerY = 100.5f;
- when(mMockFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
- scale, centerX, centerY, true, serviceId)).thenReturn(true);
- when(mMockFullScreenMagnificationController.isRegistered(TEST_DISPLAY)).thenReturn(false);
+ public void getCurrentMode_configDefaultMode_returnActivatedMode() {
+ final int targetMode = WINDOW_MODE;
+ setMagnificationActivated(TEST_DISPLAY, targetMode);
+
+ int currentMode = mMagnificationProcessor.getControllingMode(TEST_DISPLAY);
+
+ assertEquals(WINDOW_MODE, currentMode);
+ }
+
+ @Test
+ public void reset_fullscreenMagnificationActivated() {
+ setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
+
+ mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+
+ verify(mMockFullScreenMagnificationController).reset(TEST_DISPLAY, false);
+ }
- final boolean result = mMagnificationProcessor.setScaleAndCenter(
- TEST_DISPLAY, scale, centerX, centerY, true, serviceId);
+ @Test
+ public void reset_windowMagnificationActivated() {
+ setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+
+ mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+
+ verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
+ }
+
+ @Test
+ public void setMagnificationConfig_fullscreenModeNotRegistered_shouldRegister() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ final boolean result = mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
assertTrue(result);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
}
+
+ @Test
+ public void setMagnificationConfig_windowMode_enableMagnification() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ final boolean result = mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ assertTrue(result);
+ }
+
+ @Test
+ public void setMagnificationConfig_fullscreenEnabled_expectedConfigValues() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(FULLSCREEN_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+ // mMockFullScreenMagnificationController.unregister(TEST_DISPLAY);
+ mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
+ TEST_DISPLAY);
+
+ assertConfigEquals(config, result);
+ }
+
+ @Test
+ public void setMagnificationConfig_windowEnabled_expectedConfigValues() {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(WINDOW_MODE)
+ .setScale(TEST_SCALE)
+ .setCenterX(TEST_CENTER_X)
+ .setCenterY(TEST_CENTER_Y).build();
+ setMagnificationActivated(TEST_DISPLAY, config);
+
+ mMagnificationProcessor.setMagnificationConfig(
+ TEST_DISPLAY, config, true, SERVICE_ID);
+
+ final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
+ TEST_DISPLAY);
+
+ assertConfigEquals(config, result);
+ }
+
+ private void setMagnificationActivated(int displayId, int configMode) {
+ setMagnificationActivated(displayId,
+ new MagnificationConfig.Builder().setMode(configMode).build());
+ }
+
+ private void setMagnificationActivated(int displayId, MagnificationConfig config) {
+ when(mMockMagnificationController.isActivated(displayId, config.getMode())).thenReturn(
+ true);
+ mMagnificationProcessor.setMagnificationConfig(displayId, config, false, SERVICE_ID);
+ if (config.getMode() == FULLSCREEN_MODE) {
+ when(mMockMagnificationController.isActivated(displayId, WINDOW_MODE)).thenReturn(
+ false);
+ mFullScreenMagnificationControllerStub.resetAndStubMethods();
+ mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY(), true, SERVICE_ID);
+ } else if (config.getMode() == WINDOW_MODE) {
+ when(mMockMagnificationController.isActivated(displayId, FULLSCREEN_MODE)).thenReturn(
+ false);
+ mWindowMagnificationManagerStub.resetAndStubMethods();
+ mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
+ config.getCenterX(), config.getCenterY());
+ }
+ }
+
+ private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig actual) {
+ assertEquals(expected.getMode(), actual.getMode());
+ assertEquals(expected.getScale(), actual.getScale(), 0);
+ assertEquals(expected.getCenterX(), actual.getCenterX(), 0);
+ assertEquals(expected.getCenterY(), actual.getCenterY(), 0);
+ }
+
+ private static class FullScreenMagnificationControllerStub {
+ private final FullScreenMagnificationController mScreenMagnificationController;
+ private float mScale = 1.0f;
+ private float mCenterX = 0;
+ private float mCenterY = 0;
+ private boolean mIsRegistered = false;
+
+ FullScreenMagnificationControllerStub(
+ FullScreenMagnificationController screenMagnificationController) {
+ mScreenMagnificationController = screenMagnificationController;
+ }
+
+ private void stubMethods() {
+ doAnswer(invocation -> mScale).when(mScreenMagnificationController).getScale(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterX).when(mScreenMagnificationController).getCenterX(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterY).when(mScreenMagnificationController).getCenterY(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mIsRegistered).when(mScreenMagnificationController).isRegistered(
+ TEST_DISPLAY);
+ Answer enableMagnificationStubAnswer = invocation -> {
+ mScale = invocation.getArgument(1);
+ mCenterX = invocation.getArgument(2);
+ mCenterY = invocation.getArgument(3);
+ return true;
+ };
+ doAnswer(enableMagnificationStubAnswer).when(
+ mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(),
+ anyFloat(), anyFloat(), eq(true), eq(SERVICE_ID));
+
+ Answer registerStubAnswer = invocation -> {
+ mIsRegistered = true;
+ return true;
+ };
+ doAnswer(registerStubAnswer).when(
+ mScreenMagnificationController).register(eq(TEST_DISPLAY));
+
+ Answer unregisterStubAnswer = invocation -> {
+ mIsRegistered = false;
+ return true;
+ };
+ doAnswer(unregisterStubAnswer).when(
+ mScreenMagnificationController).unregister(eq(TEST_DISPLAY));
+ }
+
+ public void resetAndStubMethods() {
+ Mockito.reset(mScreenMagnificationController);
+ stubMethods();
+ }
+ }
+
+ private static class WindowMagnificationManagerStub {
+ private final WindowMagnificationManager mWindowMagnificationManager;
+ private float mScale = 1.0f;
+ private float mCenterX = 0;
+ private float mCenterY = 0;
+
+ WindowMagnificationManagerStub(
+ WindowMagnificationManager windowMagnificationManager) {
+ mWindowMagnificationManager = windowMagnificationManager;
+ }
+
+ private void stubMethods() {
+ doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+ TEST_DISPLAY);
+ doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+ TEST_DISPLAY);
+ Answer enableWindowMagnificationStubAnswer = invocation -> {
+ mScale = invocation.getArgument(1);
+ mCenterX = invocation.getArgument(2);
+ mCenterY = invocation.getArgument(3);
+ return true;
+ };
+ doAnswer(enableWindowMagnificationStubAnswer).when(
+ mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ anyFloat(), anyFloat(), anyFloat());
+ }
+
+ public void resetAndStubMethods() {
+ Mockito.reset(mWindowMagnificationManager);
+ stubMethods();
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 15ba358f146e..02c0aca5c777 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -377,6 +377,17 @@ public class WindowMagnificationManagerTest {
}
@Test
+ public void resetMagnification_enabled_windowMagnifierDisabled() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+
+ mWindowMagnificationManager.reset(TEST_DISPLAY);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
throws RemoteException {
mWindowMagnificationManager.requestConnection(true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 7aea392e37ec..6a85c8b2e2ba 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -27,11 +29,17 @@ import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.IIntentReceiver;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.util.SparseArray;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.HexDump;
@@ -42,6 +50,7 @@ import com.google.android.collect.Lists;
import org.junit.After;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,6 +71,11 @@ import java.util.regex.Pattern;
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@RunWith(AndroidJUnit4.class)
public class PackageManagerServiceTest {
+
+ private static final String TEST_DATA_PATH = "/data/local/tmp/servicestests/";
+ private static final String TEST_APP_APK = "StubTestApp.apk";
+ private static final String TEST_PKG_NAME = "com.android.servicestests.apps.stubapp";
+
@Before
public void setUp() throws Exception {
}
@@ -603,4 +617,119 @@ public class PackageManagerServiceTest {
Collections.sort(knownPackageIds);
return knownPackageIds;
}
+
+ @Test
+ public void testInstallReason_afterUpdate_keepUnchanged() throws Exception {
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ try {
+ // Try to install test APK with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason of test APK is incorrect.").that(
+ pm.getInstallReason(TEST_PKG_NAME, UserHandle.myUserId())).isEqualTo(
+ PackageManager.INSTALL_REASON_POLICY);
+
+ // Try to update test APK with different reason INSTALL_REASON_USER
+ runShellCommand("pm install --install-reason 4 " + testApk);
+ assertWithMessage("The install reason should keep unchanged after update.").that(
+ pm.getInstallReason(TEST_PKG_NAME, UserHandle.myUserId())).isEqualTo(
+ PackageManager.INSTALL_REASON_POLICY);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
+ }
+
+ @Test
+ public void testInstallReason_userRemainsUninstalled_keepUnknown() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Try to install test APK with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason of test APK is incorrect.").that(
+ pm.getInstallReason(TEST_PKG_NAME, UserHandle.myUserId())).isEqualTo(
+ PackageManager.INSTALL_REASON_POLICY);
+
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+ // Since the test APK isn't installed on the 2nd user, the reason should be unknown.
+ assertWithMessage("The install reason in 2nd user should be unknown.").that(
+ pm.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_UNKNOWN);
+
+ // Try to update test APK with different reason INSTALL_REASON_USER
+ runShellCommand("pm install --install-reason 4 " + testApk);
+ assertWithMessage("The install reason in 2nd user should keep unknown.").that(
+ pm.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_UNKNOWN);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
+
+ @Test
+ public void testInstallReason_installForAllUsers_sameReason() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+
+ // Try to install test APK to all users with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --install-reason 1 " + testApk);
+ assertWithMessage("The install reason is inconsistent across users.").that(
+ pm.getInstallReason(TEST_PKG_NAME, UserHandle.myUserId())).isEqualTo(
+ pm.getInstallReason(TEST_PKG_NAME, userId));
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
+
+ @Test
+ public void testInstallReason_installSeparately_withSeparatedReason() throws Exception {
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final UserManager um = UserManager.get(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ int userId = UserHandle.USER_NULL;
+ try {
+ // Create and start the 2nd user.
+ userId = um.createUser("Test User", 0 /* flags */).getUserHandle().getIdentifier();
+ runShellCommand("am start-user -w " + userId);
+
+ // Try to install test APK on the current user with reason INSTALL_REASON_POLICY
+ runShellCommand("pm install --user cur --install-reason 1 " + testApk);
+ assertWithMessage("The install reason on the current user is incorrect.").that(
+ pm.getInstallReason(TEST_PKG_NAME, UserHandle.myUserId())).isEqualTo(
+ PackageManager.INSTALL_REASON_POLICY);
+
+ // Try to install test APK on the 2nd user with reason INSTALL_REASON_USER
+ runShellCommand("pm install --user " + userId + " --install-reason 4 " + testApk);
+ assertWithMessage("The install reason on the 2nd user is incorrect.").that(
+ pm.getInstallReason(TEST_PKG_NAME, userId)).isEqualTo(
+ PackageManager.INSTALL_REASON_USER);
+ } finally {
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ if (userId != UserHandle.USER_NULL) {
+ um.removeUser(userId);
+ }
+ }
+ }
}
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 5d93e3dfadde..ab37e9bf6deb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -25,6 +25,7 @@ import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static android.content.res.Resources.ID_NULL;
+import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -86,7 +87,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
@@ -1004,6 +1004,13 @@ public class PackageManagerSettingsTests {
}
}
+ private void verifyKeySetData(PackageKeySetData originalData, PackageKeySetData testData) {
+ assertThat(originalData.getProperSigningKeySet(),
+ equalTo(testData.getProperSigningKeySet()));
+ assertThat(originalData.getUpgradeKeySets(), is(testData.getUpgradeKeySets()));
+ assertThat(originalData.getAliases(), is(testData.getAliases()));
+ }
+
private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
assertThat(origPkgSetting, is(not(testPkgSetting)));
assertThat(origPkgSetting.getAppId(), is(testPkgSetting.getAppId()));
@@ -1018,8 +1025,7 @@ public class PackageManagerSettingsTests {
assertSame(origPkgSetting.getInstallSource(), testPkgSetting.getInstallSource());
assertThat(origPkgSetting.isInstallPermissionsFixed(),
is(testPkgSetting.isInstallPermissionsFixed()));
- assertSame(origPkgSetting.getKeySetData(), testPkgSetting.getKeySetData());
- assertThat(origPkgSetting.getKeySetData(), is(testPkgSetting.getKeySetData()));
+ verifyKeySetData(origPkgSetting.getKeySetData(), testPkgSetting.getKeySetData());
assertThat(origPkgSetting.getLastUpdateTime(), is(testPkgSetting.getLastUpdateTime()));
assertSame(origPkgSetting.getLegacyNativeLibraryPath(),
testPkgSetting.getLegacyNativeLibraryPath());
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 7d24a2f5845e..a9cbad27c4c5 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -16,6 +16,19 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+import static android.os.Vibrator.VIBRATION_INTENSITY_HIGH;
+import static android.os.Vibrator.VIBRATION_INTENSITY_LOW;
+import static android.os.Vibrator.VIBRATION_INTENSITY_MEDIUM;
+import static android.os.Vibrator.VIBRATION_INTENSITY_OFF;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -39,9 +52,7 @@ import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
-import android.os.VibrationAttributes;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
@@ -70,16 +81,19 @@ import org.mockito.junit.MockitoRule;
public class VibrationSettingsTest {
private static final int UID = 1;
- private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
.setBatterySaverEnabled(true).build();
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
- @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock
+ private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
+ @Mock
+ private PowerManagerInternal mPowerManagerInternalMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
@@ -129,14 +143,12 @@ public class VibrationSettingsTest {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- verify(mListenerMock, times(7)).onChange();
+ verify(mListenerMock, times(8)).onChange();
}
@Test
@@ -171,89 +183,83 @@ public class VibrationSettingsTest {
VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
- assertFalse(vibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertFalse(vibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withoutRingtoneUsage_returnsTrue() {
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withVibrateWhenRinging_ignoreSettingsForSilentMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withApplyRampingRinger_ignoreSettingsForSilentMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withAllSettingsOff_onlyVibratesForVibrateMode() {
- int usageRingtone = VibrationAttributes.USAGE_RINGTONE;
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(usageRingtone));
+ assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() {
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertFalse(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_TOUCH));
+ assertFalse(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
}
@Test
@@ -261,38 +267,32 @@ public class VibrationSettingsTest {
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID,
- VibrationAttributes.USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, VibrationAttributes.USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_RINGTONE));
}
@Test
public void shouldVibrateForPowerMode_withLowPowerAndAllowedUsage_returnTrue() {
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_COMMUNICATION_REQUEST));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_ALARM));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_RINGTONE));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_COMMUNICATION_REQUEST));
}
@Test
public void shouldVibrateForPowerMode_withRestrictedUsage_returnsFalseWhileInLowPowerMode() {
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
+ assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(VibrationAttributes.USAGE_TOUCH));
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(
- VibrationAttributes.USAGE_NOTIFICATION));
+ assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
+ assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
}
@Test
@@ -324,108 +324,128 @@ public class VibrationSettingsTest {
@Test
public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_HIGH);
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- vibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ vibrationSettings.getDefaultIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ vibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
}
@Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-
- setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_OFF);
-
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getDefaultIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_MEDIUM);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_LOW);
+
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
}
@Test
public void getCurrentIntensity_returnsIntensityFromSettings() {
- mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_OFF);
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_MEDIUM);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_LOW);
-
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_ALARM));
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_UNKNOWN));
- assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
- mVibrationSettings.getCurrentIntensity(
- VibrationAttributes.USAGE_PHYSICAL_EMULATION));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+
+ assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_ALARM));
+ assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_NOTIFICATION));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_UNKNOWN));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
}
@Test
public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
- mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
- Vibrator.VIBRATION_INTENSITY_HIGH);
- assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+
+ // Switching user is not working with FakeSettingsProvider.
+ // Testing the broadcast flow manually.
+ Settings.System.putIntForUser(mContextSpy.getContentResolver(),
+ Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+ UserHandle.USER_CURRENT);
+ mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_USER_SWITCHED));
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+ }
+
+ @Test
+ public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
+ mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ // If haptic feedback is off, fallback to default value.
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_MEDIUM,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
// Switching user is not working with FakeSettingsProvider.
// Testing the broadcast flow manually.
Settings.System.putIntForUser(mContextSpy.getContentResolver(),
- Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW,
+ Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH,
UserHandle.USER_CURRENT);
mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
new Intent(Intent.ACTION_USER_SWITCHED));
- assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
- mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+ assertEquals(VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
}
@Test
diff --git a/services/tests/servicestests/test-apps/StubApp/Android.bp b/services/tests/servicestests/test-apps/StubApp/Android.bp
new file mode 100644
index 000000000000..99deb3f5bbf0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "StubTestApp",
+
+ sdk_version: "current",
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
new file mode 100644
index 000000000000..90172e77f958
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.stubapp">
+
+ <application android:label="StubTestApp">
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
new file mode 100644
index 000000000000..0d94676aeb52
--- /dev/null
+++ b/services/tests/servicestests/test-apps/StubApp/src/com/android/servicestests/apps/stubapp/TestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.servicestests.apps.stubapp;
+
+import android.app.Activity;
+
+public class TestActivity extends Activity {
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 58089648ceca..ea3a4cd865cb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -8511,6 +8511,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ // using the style, but incorrect type in session - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
+ nb.addExtras(extras);
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index ea0196306d9f..29ef339a6382 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -89,6 +89,7 @@ import android.media.AudioManager;
import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
@@ -746,6 +747,18 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+ // using the style, but incorrect type in session - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
+ nb.addExtras(extras);
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
// style + media session - bypasses block
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 1266b2e30859..afc2b8748abb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -854,12 +854,19 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
DEFAULT_USER_ID);
+
+ // committing empty locales, when no config is set should return false.
+ assertFalse(packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()).commit());
+
// committing new configuration returns true;
assertTrue(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
.commit());
// applying the same configuration returns false.
assertFalse(packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
.commit());
+
+ // committing empty locales and undefined nightMode should return true (deletes the
+ // pre-existing record) if some config was previously set.
assertTrue(packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList())
.setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit());
}
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 5d0e34a80f3f..6fa306b004a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -803,6 +803,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
+ task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
@@ -879,6 +880,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
closingActivity.allDrawn = true;
closingActivity.info.applicationInfo.uid = 12345;
+ task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
openingActivity.info.applicationInfo.uid = 54321;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 7e5414e8a5c2..a5c6dc0414dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -352,28 +353,66 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
- public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+ public void testApplyTransaction_enforceHierarchyChange_createTaskFragment()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
mOrganizer.applyTransaction(mTransaction);
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
- final TaskFragmentCreationParams mockParams = mock(TaskFragmentCreationParams.class);
- doReturn(mOrganizerToken).when(mockParams).getOrganizer();
- mTransaction.createTaskFragment(mockParams);
+ mTransaction.createTaskFragment(params);
mTransaction.startActivityInTaskFragment(
mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
null /* options */);
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
- // testing the security check here.
- assertThrows(IllegalArgumentException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ // Successfully created a TaskFragment.
+ final TaskFragment taskFragment = mAtm.mWindowOrganizerController
+ .getTaskFragment(fragmentToken);
+ assertNotNull(taskFragment);
+ assertEquals(activity.getTask(), taskFragment.getTask());
+ }
+
+ @Test
+ public void testApplyTransaction_createTaskFragment_failForDifferentUid()
+ throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final int uid = Binder.getCallingUid();
+ final IBinder fragmentToken = new Binder();
+ final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
+ mOrganizerToken, fragmentToken, activity.token).build();
+ mOrganizer.applyTransaction(mTransaction);
+ mTransaction.createTaskFragment(params);
+
+ // Fail to create TaskFragment when the task uid is different from caller.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid + 1;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Fail to create TaskFragment when the task uid is different from owner activity.
+ activity.info.applicationInfo.uid = uid + 1;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+
+ // Successfully created a TaskFragment for same uid.
+ activity.info.applicationInfo.uid = uid;
+ activity.getTask().effectiveUid = uid;
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+ assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index f366f57bae08..caaf4e495e63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -54,7 +54,6 @@ import android.view.RoundedCorners;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.window.ITransitionPlayer;
import androidx.test.filters.SmallTest;
@@ -313,11 +312,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
wallpaperWindow.setHasSurface(true);
// Set-up mock shell transitions
- final IBinder mockBinder = mock(IBinder.class);
- final ITransitionPlayer mockPlayer = mock(ITransitionPlayer.class);
- doReturn(mockBinder).when(mockPlayer).asBinder();
- mWm.mAtmService.getTransitionController().registerTransitionPlayer(mockPlayer,
- null /* appThread */);
+ registerTestTransitionPlayer();
Transition transit =
mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
@@ -338,10 +333,21 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertFalse(token.isVisibleRequested());
assertTrue(token.isVisible());
- transit.onTransactionReady(transit.getSyncId(), mock(SurfaceControl.Transaction.class));
- transit.finishTransition();
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ token.finishSync(t, false /* cancel */);
+ transit.onTransactionReady(transit.getSyncId(), t);
+ dc.mTransitionController.finishTransition(transit);
assertFalse(wallpaperWindow.isVisible());
assertFalse(token.isVisible());
+
+ // Assume wallpaper was visible. When transaction is ready without wallpaper target,
+ // wallpaper should be requested to be invisible.
+ token.setVisibility(true);
+ transit = dc.mTransitionController.createTransition(TRANSIT_CLOSE);
+ dc.mTransitionController.collect(token);
+ transit.onTransactionReady(transit.getSyncId(), t);
+ assertFalse(token.isVisibleRequested());
+ assertTrue(token.isVisible());
}
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index abbce1cd869d..98f619f497d4 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1687,7 +1687,11 @@ public class TelecomManager {
*
* @param accountHandle The handle for the account retrieve a number for.
* @return A string representation of the line 1 phone number.
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
+ * Telephony Subscription ID that can be retrieved with the {@code accountHandle}
+ * from {@link TelephonyManager#getSubscriptionId(PhoneAccountHandle)}.
*/
+ @Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
diff --git a/telephony/common/Android.bp b/telephony/common/Android.bp
index 1cacc0365fe1..b0a812bf57a3 100644
--- a/telephony/common/Android.bp
+++ b/telephony/common/Android.bp
@@ -21,7 +21,10 @@ filegroup {
filegroup {
name: "framework-mms-shared-srcs",
- visibility: ["//packages/apps/Bluetooth"],
+ visibility: [
+ "//packages/apps/Bluetooth",
+ "//packages/modules/Bluetooth/android/app",
+ ],
srcs: [
"com/google/android/mms/**/*.java",
],
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 7d857a2dc1b4..fabe612743bb 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -856,9 +856,10 @@ public abstract class EuiccService extends Service {
mExecutor.execute(new Runnable() {
@Override
public void run() {
+ // TODO(b/207392528: use portIndex API once implemented)
int result =
- EuiccService.this.onSwitchToSubscriptionWithPort(
- slotId, portIndex, iccid, forceDeactivateSim);
+ EuiccService.this.onSwitchToSubscription(
+ slotId, iccid, forceDeactivateSim);
try {
callback.onComplete(result);
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 23cf5116b2da..e88106cb95fe 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,6 +1,8 @@
package android.telephony;
import android.annotation.IntDef;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.telecom.Connection;
import android.telephony.data.ApnSetting;
@@ -664,4 +666,59 @@ public class Annotation {
TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE,
TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR})
public @interface ThermalMitigationResult {}
+
+ /**
+ * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+ * android.net.NetworkCapabilities.NetCapability here. Must update here when new capabilities
+ * are added in {@link NetworkCapabilities}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_WIFI_P2P,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED,
+ NetworkCapabilities.NET_CAPABILITY_TRUSTED,
+ NetworkCapabilities.NET_CAPABILITY_NOT_VPN,
+ NetworkCapabilities.NET_CAPABILITY_VALIDATED,
+ NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL,
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
+ NetworkCapabilities.NET_CAPABILITY_FOREGROUND,
+ NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED,
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED,
+ NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
+ NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED,
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+ NetworkCapabilities.NET_CAPABILITY_VSIM,
+ NetworkCapabilities.NET_CAPABILITY_BIP,
+ NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT,
+ })
+ public @interface NetCapability { }
+
+ /**
+ * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+ * android.net.NetworkAgent.ValidationStatus here. Must update here when new validation status
+ * are added in {@link NetworkAgent}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+ NetworkAgent.VALIDATION_STATUS_VALID,
+ NetworkAgent.VALIDATION_STATUS_NOT_VALID
+ })
+ public @interface ValidationStatus {}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3f0f50c5f62a..c7e5aafe23e8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5951,9 +5951,9 @@ public class CarrierConfigManager {
"capabilities=eims, retry_interval=1000, maximum_retries=20",
"fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|"
+ "2254, maximum_retries=0", // No retry for those causes
+ "capabilities=mms|supl|cbs, retry_interval=2000",
"capabilities=internet|enterprise|dun|ims|fota, retry_interval=2000, "
+ "backoff=true, maximum_retries=13",
- "capabilities=mms|supl|cbs, retry_interval=2000"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 88efe1f6a4a7..56bf3039d209 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1076,6 +1076,13 @@ public final class DataFailCause {
*/
public static final int SERVICE_TEMPORARILY_UNAVAILABLE = 0x10009;
+ /**
+ * The request is not supported by the vendor.
+ *
+ * @hide
+ */
+ public static final int REQUEST_NOT_SUPPORTED = 0x1000A;
+
private static final Map<Integer, String> sFailCauseMap;
static {
sFailCauseMap = new HashMap<>();
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 2ff4ac5e30d3..9cb80f1814f9 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -71,12 +71,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
@Nullable List<SignalThresholdInfo> signalThresholdInfos,
boolean isReportingRequestedWhileIdle,
boolean isSystemThresholdReportingRequestedWhileIdle) {
- // System app (like Bluetooth) can specify the request to report system thresholds while
- // device is idle (with permission protection). In this case, the request doesn't need to
- // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
- if (!isSystemThresholdReportingRequestedWhileIdle) {
- validate(signalThresholdInfos);
- }
+ validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle);
mSignalThresholdInfos = signalThresholdInfos;
mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
@@ -274,8 +269,12 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
* Throw IAE if SignalThresholdInfo collection is null or empty,
* or the SignalMeasurementType for the same RAN in the collection is not unique.
*/
- private static void validate(Collection<SignalThresholdInfo> infos) {
- if (infos == null || infos.isEmpty()) {
+ private static void validate(Collection<SignalThresholdInfo> infos,
+ boolean isSystemThresholdReportingRequestedWhileIdle) {
+ // System app (like Bluetooth) can specify the request to report system thresholds while
+ // device is idle (with permission protection). In this case, the request doesn't need to
+ // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
+ if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) {
throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d6d6775cb40f..d11ad914061d 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -498,7 +498,10 @@ public class SubscriptionInfo implements Parcelable {
*
* @return the number of this subscription, or an empty string if one of these requirements is
* not met
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
+ * {@link #getSubscriptionId() subscription ID}.
*/
+ @Deprecated
public String getNumber() {
return mNumber;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 04e7b7c8711c..88b21e0428b0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4791,7 +4791,10 @@ public class TelephonyManager {
* for any API level.
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* for apps targeting SDK API level 29 and below.
+ *
+ * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
*/
+ @Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
@@ -4864,7 +4867,9 @@ public class TelephonyManager {
* @param alphaTag alpha-tagging of the dailing nubmer
* @param number The dialing number
* @return true if the operation was executed correctly.
+ * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
*/
+ @Deprecated
public boolean setLine1NumberForDisplay(String alphaTag, String number) {
return setLine1NumberForDisplay(getSubId(), alphaTag, number);
}
@@ -4885,6 +4890,10 @@ public class TelephonyManager {
*/
public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
try {
+ // This API is deprecated; call the new API to allow smooth migartion.
+ // The new API doesn't accept null so convert null to empty string.
+ mSubscriptionManager.setCarrierPhoneNumber(subId, (number == null ? "" : number));
+
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 93903d2658cd..43ad982f2e1e 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -22,9 +22,11 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.NetworkCapabilities;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetCapability;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
import android.telephony.data.ApnSetting.AuthType;
@@ -66,7 +68,7 @@ public final class DataProfile implements Parcelable {
private final @Nullable TrafficDescriptor mTrafficDescriptor;
- private final boolean mPreferred;
+ private boolean mPreferred;
private DataProfile(@NonNull Builder builder) {
mApnSetting = builder.mApnSetting;
@@ -291,6 +293,16 @@ public final class DataProfile implements Parcelable {
}
/**
+ * Set the preferred flag for the data profile.
+ *
+ * @param preferred {@code true} if this data profile is preferred for internet.
+ * @hide
+ */
+ public void setPreferred(boolean preferred) {
+ mPreferred = preferred;
+ }
+
+ /**
* @return {@code true} if this data profile was used to bring up the last default
* (i.e internet) data connection successfully, or the one chosen by the user in Settings'
* APN editor. For one carrier there can be only one profiled preferred.
@@ -315,6 +327,76 @@ public final class DataProfile implements Parcelable {
return mTrafficDescriptor;
}
+ /**
+ * Check if this data profile can satisfy certain network capabilities
+ *
+ * @param networkCapabilities The network capabilities. Note that the non-APN-type capabilities
+ * will be ignored.
+ *
+ * @return {@code true} if this data profile can satisfy the given network capabilities.
+ * @hide
+ */
+ public boolean canSatisfy(@NonNull @NetCapability int[] networkCapabilities) {
+ if (mApnSetting != null) {
+ for (int netCap : networkCapabilities) {
+ if (!canSatisfy(netCap)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if this data profile can satisfy a certain network capability.
+ *
+ * @param networkCapability The network capability. Note that the non-APN-type capability
+ * will always be satisfied.
+ * @return {@code true} if this data profile can satisfy the given network capability.
+ * @hide
+ */
+ public boolean canSatisfy(@NetCapability int networkCapability) {
+ return mApnSetting != null && mApnSetting.canHandleType(
+ networkCapabilityToApnType(networkCapability));
+ }
+
+ /**
+ * Convert network capability into APN type.
+ *
+ * @param networkCapability Network capability.
+ * @return APN type.
+ * @hide
+ */
+ private static @ApnType int networkCapabilityToApnType(@NetCapability int networkCapability) {
+ switch (networkCapability) {
+ case NetworkCapabilities.NET_CAPABILITY_MMS:
+ return ApnSetting.TYPE_MMS;
+ case NetworkCapabilities.NET_CAPABILITY_SUPL:
+ return ApnSetting.TYPE_SUPL;
+ case NetworkCapabilities.NET_CAPABILITY_DUN:
+ return ApnSetting.TYPE_DUN;
+ case NetworkCapabilities.NET_CAPABILITY_FOTA:
+ return ApnSetting.TYPE_FOTA;
+ case NetworkCapabilities.NET_CAPABILITY_IMS:
+ return ApnSetting.TYPE_IMS;
+ case NetworkCapabilities.NET_CAPABILITY_CBS:
+ return ApnSetting.TYPE_CBS;
+ case NetworkCapabilities.NET_CAPABILITY_XCAP:
+ return ApnSetting.TYPE_XCAP;
+ case NetworkCapabilities.NET_CAPABILITY_EIMS:
+ return ApnSetting.TYPE_EMERGENCY;
+ case NetworkCapabilities.NET_CAPABILITY_INTERNET:
+ return ApnSetting.TYPE_DEFAULT;
+ case NetworkCapabilities.NET_CAPABILITY_MCX:
+ return ApnSetting.TYPE_MCX;
+ case NetworkCapabilities.NET_CAPABILITY_IA:
+ return ApnSetting.TYPE_IA;
+ default:
+ return ApnSetting.TYPE_NONE;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index c00c741a0d60..1b1040430fd2 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -117,6 +117,7 @@ public final class DelegateRegistrationState implements Parcelable {
})
public @interface DeregisteringReason {}
+ private ArraySet<String> mRegisteringTags = new ArraySet<>();
private ArraySet<String> mRegisteredTags = new ArraySet<>();
private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>();
private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>();
@@ -134,6 +135,20 @@ public final class DelegateRegistrationState implements Parcelable {
}
/**
+ * Add the set of feature tags that are associated with this SipDelegate and
+ * the IMS stack is actively trying to register on the carrier network.
+ *
+ * The feature tags will either move to the registered or deregistered state
+ * depending on the result of the registration.
+ * @param featureTags The IMS media feature tags that are in the progress of registering.
+ * @return The in-progress Builder instance for RegistrationState. ]
+ */
+ public @NonNull Builder addRegisteringFeatureTags(@NonNull Set<String> featureTags) {
+ mState.mRegisteringTags.addAll(featureTags);
+ return this;
+ }
+
+ /**
* Add a feature tag that is currently included in the current network IMS Registration.
* @param featureTag The IMS media feature tag included in the current IMS registration.
* @return The in-progress Builder instance for RegistrationState.
@@ -209,6 +224,17 @@ public final class DelegateRegistrationState implements Parcelable {
mRegisteredTags = (ArraySet<String>) source.readArraySet(null);
readStateFromParcel(source, mDeregisteringTags);
readStateFromParcel(source, mDeregisteredTags);
+ mRegisteringTags = (ArraySet<String>) source.readArraySet(null);
+ }
+
+ /**
+ * Get the feature tags that are associated with this SipDelegate that the IMS stack is actively
+ * trying to register on the carrier network.
+ * @return A Set of feature tags associated with this SipDelegate that the IMS service is
+ * currently trying to register on the carrier network.
+ */
+ public @NonNull Set<String> getRegisteringFeatureTags() {
+ return new ArraySet<>(mRegisteringTags);
}
/**
@@ -286,6 +312,7 @@ public final class DelegateRegistrationState implements Parcelable {
dest.writeArraySet(mRegisteredTags);
writeStateToParcel(dest, mDeregisteringTags);
writeStateToParcel(dest, mDeregisteredTags);
+ dest.writeArraySet(mRegisteringTags);
}
private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) {
@@ -311,19 +338,22 @@ public final class DelegateRegistrationState implements Parcelable {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DelegateRegistrationState that = (DelegateRegistrationState) o;
- return mRegisteredTags.equals(that.mRegisteredTags)
+ return mRegisteringTags.equals(that.mRegisteringTags)
+ && mRegisteredTags.equals(that.mRegisteredTags)
&& mDeregisteringTags.equals(that.mDeregisteringTags)
&& mDeregisteredTags.equals(that.mDeregisteredTags);
}
@Override
public int hashCode() {
- return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags);
+ return Objects.hash(mRegisteringTags, mRegisteredTags,
+ mDeregisteringTags, mDeregisteredTags);
}
@Override
public String toString() {
return "DelegateRegistrationState{ registered={" + mRegisteredTags
+ + "}, registering={" + mRegisteringTags
+ "}, deregistering={" + mDeregisteringTags + "}, deregistered={"
+ mDeregisteredTags + "}}";
}