summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java235
-rw-r--r--cmds/bootanimation/BootAnimation.cpp28
-rw-r--r--cmds/bootanimation/BootAnimation.h2
-rw-r--r--core/api/current.txt15
-rw-r--r--core/api/system-current.txt36
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java157
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl3
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl4
-rw-r--r--core/java/android/accounts/AccountManager.java8
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/ApplicationPackageManager.java4
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java10
-rw-r--r--core/java/android/app/UiAutomation.java3
-rw-r--r--core/java/android/app/WtfException.java66
-rw-r--r--core/java/android/app/compat/ChangeIdStateCache.java2
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java15
-rwxr-xr-xcore/java/android/bluetooth/BluetoothClass.java1
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java2
-rw-r--r--core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java140
-rw-r--r--core/java/android/bluetooth/BluetoothLeCall.java285
-rw-r--r--core/java/android/bluetooth/BluetoothLeCallControl.java911
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java9
-rw-r--r--core/java/android/content/ContentResolver.java3
-rw-r--r--core/java/android/content/pm/PackageManager.java14
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java2
-rw-r--r--core/java/android/hardware/camera2/utils/SurfaceUtils.java2
-rw-r--r--core/java/android/hardware/display/DisplayManager.java11
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java17
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl3
-rw-r--r--core/java/android/hardware/face/FaceManager.java49
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl9
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java30
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/hardware/input/InputManager.java8
-rw-r--r--core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java (renamed from core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java)20
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java30
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java (renamed from core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java)16
-rw-r--r--core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java (renamed from core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java)22
-rw-r--r--core/java/android/os/BatteryStats.java5
-rw-r--r--core/java/android/os/BatteryStatsManager.java16
-rw-r--r--core/java/android/os/NewUserResponse.java7
-rw-r--r--core/java/android/os/PowerManager.java4
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/os/WakeLockStats.aidl20
-rw-r--r--core/java/android/os/WakeLockStats.java131
-rw-r--r--core/java/android/os/storage/StorageVolume.java20
-rw-r--r--core/java/android/os/storage/VolumeInfo.java6
-rw-r--r--core/java/android/os/storage/VolumeRecord.java6
-rw-r--r--core/java/android/permission/PermissionManager.java6
-rw-r--r--core/java/android/provider/BlockedNumberContract.java16
-rw-r--r--core/java/android/provider/CallLog.java7
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/contentsuggestions/ContentSuggestionsService.java1
-rw-r--r--core/java/android/text/BoringLayout.java149
-rw-r--r--core/java/android/text/Layout.java33
-rw-r--r--core/java/android/text/StaticLayout.java14
-rw-r--r--core/java/android/text/TextLine.java37
-rw-r--r--core/java/android/text/TextShaper.java3
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java5
-rw-r--r--core/java/android/view/SurfaceControl.java15
-rw-r--r--core/java/android/view/SurfaceView.java4
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl9
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/android/widget/TextView.java81
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl5
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java12
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java32
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl4
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java2
-rw-r--r--core/java/com/android/server/OWNERS2
-rw-r--r--core/jni/android_view_SurfaceControl.cpp11
-rw-r--r--core/proto/android/providers/settings/secure.proto2
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/styles_device_defaults.xml2
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java12
-rw-r--r--core/tests/coretests/src/android/text/TextLineTest.java13
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java52
-rw-r--r--core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java2
-rw-r--r--graphics/java/android/graphics/Paint.java136
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java2
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_mode_hint.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_ui_layout.xml10
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java103
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt40
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java4
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp10
-rw-r--r--libs/hwui/hwui/MinikinUtils.h4
-rw-r--r--libs/hwui/jni/Paint.cpp87
-rw-r--r--location/java/android/location/LocationManager.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java14
-rw-r--r--packages/ConnectivityT/framework-t/Android.bp14
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java (renamed from core/java/com/android/server/NetworkManagementSocketTagger.java)3
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java7
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java19
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java4
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/TEST_MAPPING9
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt117
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java5
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complications_layer.xml50
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_container.xml2
-rw-r--r--packages/SystemUI/res/layout/keyguard_media_container.xml (renamed from packages/SystemUI/res/layout/keyguard_media_header.xml)2
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt70
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt183
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt192
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java71
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java42
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java22
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java22
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java125
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java4
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java9
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java9
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java60
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java19
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java15
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java421
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java419
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/Interruptable.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java33
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java3
-rw-r--r--services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java117
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java4
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java39
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java51
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java8
-rw-r--r--services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java6
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java33
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java70
-rw-r--r--services/core/java/com/android/server/pm/Computer.java6
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java7
-rw-r--r--services/core/java/com/android/server/pm/ComputerLocked.java10
-rw-r--r--services/core/java/com/android/server/pm/ComputerTracker.java10
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java34
-rw-r--r--services/core/java/com/android/server/pm/OWNERS2
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java20
-rw-r--r--services/core/java/com/android/server/pm/ReconcileRequest.java18
-rw-r--r--services/core/java/com/android/server/pm/Settings.java4
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java265
-rw-r--r--services/core/java/com/android/server/pm/SharedLibraryHelper.java366
-rw-r--r--services/core/java/com/android/server/pm/SharedLibraryUtils.java105
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java67
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java5
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java7
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java103
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java30
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java6
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java113
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java18
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java6
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java1
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/Task.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java30
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java47
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt449
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java85
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java117
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java326
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java266
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java1
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java675
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java3
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java10
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java7
-rw-r--r--telephony/java/android/telephony/CallQuality.java417
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java6
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java4
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java8
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneConstants.java1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt20
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml7
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml30
-rw-r--r--tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java (renamed from tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java)20
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java18
-rw-r--r--tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java (renamed from tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java)22
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java100
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java88
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java30
352 files changed, 8358 insertions, 3886 deletions
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
new file mode 100644
index 000000000000..cf94e9e0d384
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
+import static android.view.MotionEvent.TOOL_TYPE_STYLUS;
+
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Rect;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark tests for {@link HandwritingInitiator}
+ *
+ * Build/Install/Run:
+ * atest CorePerfTests:android.view.HandwritingInitiatorPerfTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class HandwritingInitiatorPerfTest {
+ private Context mContext;
+ private HandwritingInitiator mHandwritingInitiator;
+ private int mTouchSlop;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Before
+ public void setup() {
+ final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);
+ mTouchSlop = viewConfiguration.getScaledTouchSlop();
+ InputMethodManager inputMethodManager = mContext.getSystemService(InputMethodManager.class);
+ mHandwritingInitiator = new HandwritingInitiator(viewConfiguration, inputMethodManager);
+ }
+
+ @Test
+ public void onTouchEvent_actionDown_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionUp_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionMove_toolTypeStylus() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final int initX = 10;
+ final int initY = 10;
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_STYLUS, initX, initY, 0);
+
+ final int x = initX + mTouchSlop;
+ final int y = initY + mTouchSlop;
+ final MotionEvent moveEvent =
+ createMotionEvent(ACTION_MOVE, TOOL_TYPE_STYLUS, x, y, 1);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_STYLUS, x, y, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+
+ mHandwritingInitiator.onTouchEvent(moveEvent);
+
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionDown_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionUp_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, 10, 10, 0);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, 11, 11, 1);
+
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onTouchEvent_actionMove_toolTypeFinger() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final int initX = 10;
+ final int initY = 10;
+ final MotionEvent downEvent =
+ createMotionEvent(ACTION_DOWN, TOOL_TYPE_FINGER, initX, initY, 0);
+
+ final int x = initX + mTouchSlop;
+ final int y = initY + mTouchSlop;
+ final MotionEvent moveEvent =
+ createMotionEvent(ACTION_MOVE, TOOL_TYPE_FINGER, x, y, 1);
+ final MotionEvent upEvent =
+ createMotionEvent(ACTION_UP, TOOL_TYPE_FINGER, x, y, 1);
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(downEvent);
+ state.resumeTiming();
+
+ mHandwritingInitiator.onTouchEvent(moveEvent);
+
+ state.pauseTiming();
+ mHandwritingInitiator.onTouchEvent(upEvent);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onInputConnectionCreated() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final View view = new View(mContext);
+ final EditorInfo editorInfo = new EditorInfo();
+ while (state.keepRunning()) {
+ mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ state.pauseTiming();
+ mHandwritingInitiator.onInputConnectionClosed(view);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void onInputConnectionClosed() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final View view = new View(mContext);
+ final EditorInfo editorInfo = new EditorInfo();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ state.resumeTiming();
+ mHandwritingInitiator.onInputConnectionClosed(view);
+ }
+ }
+
+ @Test
+ public void updateEditorBoundary() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Rect rect = new Rect(0, 0, 100, 100);
+ while (state.keepRunning()) {
+ mHandwritingInitiator.updateEditorBound(rect);
+ }
+ }
+
+ private MotionEvent createMotionEvent(int action, int toolType, int x, int y, long eventTime) {
+ MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
+ properties[0].toolType = toolType;
+
+ MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1);
+ coords[0].x = x;
+ coords[0].y = y;
+
+ return MotionEvent.obtain(0 /* downTime */, eventTime /* eventTime */, action, 1,
+ properties, coords, 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */,
+ 1 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_TOUCHSCREEN, 0 /* flags */);
+ }
+}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 05a0661914dc..1f4a64f5c7f1 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -81,18 +81,18 @@ static constexpr const char* PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE = "/product
static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";
-static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
-static const char SYSTEM_TIME_DIR_NAME[] = "time";
-static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
+static const char BOOTANIM_DATA_DIR_PATH[] = "/data/bootanim";
+static const char BOOTANIM_TIME_DIR_NAME[] = "time";
+static const char BOOTANIM_TIME_DIR_PATH[] = "/data/bootanim/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
-static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
+static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/bootanim/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
-static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
-static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
+static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/bootanim/time/time_is_accurate";
+static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/bootanim/time/time_format_12_hour";
// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
static const long long ACCURATE_TIME_EPOCH = 946684800000;
static constexpr char FONT_BEGIN_CHAR = ' ';
@@ -1741,7 +1741,7 @@ bool BootAnimation::updateIsTimeAccurate() {
}
BootAnimation::TimeCheckThread::TimeCheckThread(BootAnimation* bootAnimation) : Thread(false),
- mInotifyFd(-1), mSystemWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
+ mInotifyFd(-1), mBootAnimWd(-1), mTimeWd(-1), mBootAnimation(bootAnimation) {}
BootAnimation::TimeCheckThread::~TimeCheckThread() {
// mInotifyFd may be -1 but that's ok since we're not at risk of attempting to close a valid FD.
@@ -1784,7 +1784,7 @@ bool BootAnimation::TimeCheckThread::doThreadLoop() {
const struct inotify_event *event;
for (char* ptr = buff; ptr < buff + length; ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr;
- if (event->wd == mSystemWd && strcmp(SYSTEM_TIME_DIR_NAME, event->name) == 0) {
+ if (event->wd == mBootAnimWd && strcmp(BOOTANIM_TIME_DIR_NAME, event->name) == 0) {
addTimeDirWatch();
} else if (event->wd == mTimeWd && (strcmp(LAST_TIME_CHANGED_FILE_NAME, event->name) == 0
|| strcmp(ACCURATE_TIME_FLAG_FILE_NAME, event->name) == 0)) {
@@ -1796,12 +1796,12 @@ bool BootAnimation::TimeCheckThread::doThreadLoop() {
}
void BootAnimation::TimeCheckThread::addTimeDirWatch() {
- mTimeWd = inotify_add_watch(mInotifyFd, SYSTEM_TIME_DIR_PATH,
+ mTimeWd = inotify_add_watch(mInotifyFd, BOOTANIM_TIME_DIR_PATH,
IN_CLOSE_WRITE | IN_MOVED_TO | IN_ATTRIB);
if (mTimeWd > 0) {
// No need to watch for the time directory to be created if it already exists
- inotify_rm_watch(mInotifyFd, mSystemWd);
- mSystemWd = -1;
+ inotify_rm_watch(mInotifyFd, mBootAnimWd);
+ mBootAnimWd = -1;
}
}
@@ -1812,11 +1812,11 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
return NO_INIT;
}
- mSystemWd = inotify_add_watch(mInotifyFd, SYSTEM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
- if (mSystemWd < 0) {
+ mBootAnimWd = inotify_add_watch(mInotifyFd, BOOTANIM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
+ if (mBootAnimWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
- SLOGE("Could not add watch for %s: %s", SYSTEM_DATA_DIR_PATH, strerror(errno));
+ SLOGE("Could not add watch for %s: %s", BOOTANIM_DATA_DIR_PATH, strerror(errno));
return NO_INIT;
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 7a597da533ee..4c378cbc48bd 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -161,7 +161,7 @@ private:
void addTimeDirWatch();
int mInotifyFd;
- int mSystemWd;
+ int mBootAnimWd;
int mTimeWd;
BootAnimation* mBootAnimation;
};
diff --git a/core/api/current.txt b/core/api/current.txt
index b983617445e5..edf69449f4e9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2258,6 +2258,7 @@ package android {
field public static final int TextAppearance = 16973886; // 0x103003e
field public static final int TextAppearance_DeviceDefault = 16974253; // 0x10301ad
field public static final int TextAppearance_DeviceDefault_DialogWindowTitle = 16974264; // 0x10301b8
+ field public static final int TextAppearance_DeviceDefault_Headline;
field public static final int TextAppearance_DeviceDefault_Inverse = 16974254; // 0x10301ae
field public static final int TextAppearance_DeviceDefault_Large = 16974255; // 0x10301af
field public static final int TextAppearance_DeviceDefault_Large_Inverse = 16974256; // 0x10301b0
@@ -3130,11 +3131,13 @@ package android.accessibilityservice {
method public void addListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, @Nullable android.os.Handler);
method public float getCenterX();
method public float getCenterY();
+ method @NonNull public android.graphics.Region getCurrentMagnificationRegion();
method @Nullable public android.accessibilityservice.MagnificationConfig getMagnificationConfig();
method @NonNull public android.graphics.Region getMagnificationRegion();
method public float getScale();
method public boolean removeListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
method public boolean reset(boolean);
+ method public boolean resetCurrentMagnification(boolean);
method public boolean setCenter(float, float, boolean);
method public boolean setMagnificationConfig(@NonNull android.accessibilityservice.MagnificationConfig, boolean);
method public boolean setScale(float, boolean);
@@ -3142,6 +3145,7 @@ package android.accessibilityservice {
public static interface AccessibilityService.MagnificationController.OnMagnificationChangedListener {
method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float);
+ method public default void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, @NonNull android.accessibilityservice.MagnificationConfig);
}
public static final class AccessibilityService.ScreenshotResult {
@@ -9158,6 +9162,7 @@ package android.bluetooth {
field public static final int AUDIO = 2097152; // 0x200000
field public static final int CAPTURE = 524288; // 0x80000
field public static final int INFORMATION = 8388608; // 0x800000
+ field public static final int LE_AUDIO = 16384; // 0x4000
field public static final int LIMITED_DISCOVERABILITY = 8192; // 0x2000
field public static final int NETWORKING = 131072; // 0x20000
field public static final int OBJECT_TRANSFER = 1048576; // 0x100000
@@ -15972,6 +15977,8 @@ package android.graphics {
method public String getFontFeatureSettings();
method public float getFontMetrics(android.graphics.Paint.FontMetrics);
method public android.graphics.Paint.FontMetrics getFontMetrics();
+ method public void getFontMetricsInt(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
+ method public void getFontMetricsInt(@NonNull char[], @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
method public float getFontSpacing();
@@ -34292,6 +34299,7 @@ package android.provider {
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+ field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
field public static final String PRIORITY = "priority";
field public static final int PRIORITY_NORMAL = 0; // 0x0
@@ -41065,6 +41073,7 @@ package android.telecom {
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
+ field public static final int PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
field public static final int PRIORITY_NORMAL = 0; // 0x0
field public static final int PRIORITY_URGENT = 1; // 0x1
@@ -41695,6 +41704,7 @@ package android.telephony {
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
+ field public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.enable_support_for_eap_aka_fast_reauth_bool";
field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array";
field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int";
field public static final String KEY_EPDG_PCO_ID_IPV4_INT = "iwlan.epdg_pco_id_ipv4_int";
@@ -44646,6 +44656,7 @@ package android.text {
public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+ ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public void ellipsized(int, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
@@ -44660,9 +44671,12 @@ package android.text {
method public int getTopPadding();
method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
+ method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
+ method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
+ method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
}
@@ -44871,6 +44885,7 @@ package android.text {
method public abstract int getTopPadding();
method public final int getWidth();
method public final void increaseWidthTo(int);
+ method public boolean isFallbackLineSpacingEnabled();
method public boolean isRtlCharAt(int);
method protected final boolean isSpanned();
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 30154c8f4a73..e26d3c419039 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9530,7 +9530,7 @@ package android.os.storage {
public final class StorageVolume implements android.os.Parcelable {
method @NonNull public String getId();
- method public boolean isStub();
+ method public boolean isExternallyManaged();
}
}
@@ -11865,11 +11865,18 @@ package android.telephony {
method public int getCallDuration();
method public int getCodecType();
method public int getDownlinkCallQualityLevel();
+ method public long getMaxPlayoutDelayMillis();
method public int getMaxRelativeJitter();
+ method public long getMinPlayoutDelayMillis();
+ method public int getNumDroppedRtpPackets();
+ method public int getNumNoDataFrames();
+ method public int getNumRtpDuplicatePackets();
method public int getNumRtpPacketsNotReceived();
method public int getNumRtpPacketsReceived();
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
+ method public int getNumRtpSidPacketsRx();
+ method public int getNumVoiceFrames();
method public int getUplinkCallQualityLevel();
method public boolean isIncomingSilenceDetectedAtCallSetup();
method public boolean isOutgoingSilenceDetectedAtCallSetup();
@@ -11884,6 +11891,32 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
}
+ public static final class CallQuality.Builder {
+ ctor public CallQuality.Builder();
+ method @NonNull public android.telephony.CallQuality build();
+ method @NonNull public android.telephony.CallQuality.Builder setAverageRelativeJitter(int);
+ method @NonNull public android.telephony.CallQuality.Builder setAverageRoundTripTime(int);
+ method @NonNull public android.telephony.CallQuality.Builder setCallDuration(int);
+ method @NonNull public android.telephony.CallQuality.Builder setCodecType(int);
+ method @NonNull public android.telephony.CallQuality.Builder setDownlinkCallQualityLevel(int);
+ method @NonNull public android.telephony.CallQuality.Builder setIncomingSilenceDetectedAtCallSetup(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setMaxPlayoutDelayMillis(long);
+ method @NonNull public android.telephony.CallQuality.Builder setMaxRelativeJitter(int);
+ method @NonNull public android.telephony.CallQuality.Builder setMinPlayoutDelayMillis(long);
+ method @NonNull public android.telephony.CallQuality.Builder setNumDroppedRtpPackets(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumNoDataFrames(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpDuplicatePackets(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsNotReceived(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsReceived(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmitted(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpPacketsTransmittedLost(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumRtpSidPacketsRx(int);
+ method @NonNull public android.telephony.CallQuality.Builder setNumVoiceFrames(int);
+ method @NonNull public android.telephony.CallQuality.Builder setOutgoingSilenceDetectedAtCallSetup(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setRtpInactivityDetected(boolean);
+ method @NonNull public android.telephony.CallQuality.Builder setUplinkCallQualityLevel(int);
+ }
+
public class CarrierConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
method @NonNull public static android.os.PersistableBundle getDefaultConfig();
@@ -13785,6 +13818,7 @@ package android.telephony.ims {
field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
+ field public static final int OIR_PRESENTATION_UNAVAILABLE = 5; // 0x5
field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
field public static final int PRIORITY_NORMAL = 0; // 0x0
field public static final int PRIORITY_URGENT = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8724b5363985..0710781a3155 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1679,6 +1679,10 @@ package android.os {
method public void removeSyncBarrier(int);
}
+ public final class NewUserResponse {
+ ctor public NewUserResponse(@Nullable android.os.UserHandle, int);
+ }
+
public final class PackageTagsList implements android.os.Parcelable {
method public boolean contains(@NonNull String, @Nullable String);
method public boolean contains(@NonNull android.os.PackageTagsList);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9a5586747d0e..479e6bf594ba 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -588,7 +588,7 @@ public abstract class AccessibilityService extends Service {
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY);
+ MagnificationConfig config);
/** Callbacks for receiving motion events. */
void onMotionEvent(MotionEvent event);
/** Callback for tuch state changes. */
@@ -1183,14 +1183,14 @@ public abstract class AccessibilityService extends Service {
}
}
- private void onMagnificationChanged(int displayId, @NonNull Region region, float scale,
- float centerX, float centerY) {
+ private void onMagnificationChanged(int displayId, @NonNull Region region,
+ MagnificationConfig config) {
MagnificationController controller;
synchronized (mLock) {
controller = mMagnificationControllers.get(displayId);
}
if (controller != null) {
- controller.dispatchMagnificationChanged(region, scale, centerX, centerY);
+ controller.dispatchMagnificationChanged(region, config);
}
}
@@ -1328,8 +1328,8 @@ public abstract class AccessibilityService extends Service {
* Dispatches magnification changes to any registered listeners. This
* should be called on the service's main thread.
*/
- void dispatchMagnificationChanged(final @NonNull Region region, final float scale,
- final float centerX, final float centerY) {
+ void dispatchMagnificationChanged(final @NonNull Region region,
+ final MagnificationConfig config) {
final ArrayMap<OnMagnificationChangedListener, Handler> entries;
synchronized (mLock) {
if (mListeners == null || mListeners.isEmpty()) {
@@ -1348,16 +1348,13 @@ public abstract class AccessibilityService extends Service {
final OnMagnificationChangedListener listener = entries.keyAt(i);
final Handler handler = entries.valueAt(i);
if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onMagnificationChanged(MagnificationController.this,
- region, scale, centerX, centerY);
- }
+ handler.post(() -> {
+ listener.onMagnificationChanged(MagnificationController.this,
+ region, config);
});
} else {
// We're already on the main thread, just run the listener.
- listener.onMagnificationChanged(this, region, scale, centerX, centerY);
+ listener.onMagnificationChanged(this, region, config);
}
}
}
@@ -1503,6 +1500,12 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return an empty region.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the magnification region of full-screen
+ * magnification. To get the magnification region of the current controlling magnifier,
+ * use {@link #getCurrentMagnificationRegion()} instead.
+ * </p>
*
* @return the region of the screen currently active for magnification, or an empty region
* if magnification is not active.
@@ -1524,6 +1527,45 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Returns the region of the screen currently active for magnification if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+ * Returns the region of screen projected on the magnification window if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+ *
+ * <p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * the returned region will be empty if the magnification is
+ * not active. And the magnification is active if magnification gestures are enabled
+ * or if a service is running that can control magnification.
+ * </p><p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * the returned region will be empty if the magnification is not activated.
+ * </p><p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will
+ * return an empty region.
+ * </p>
+ *
+ * @return the magnification region of the currently controlling magnification
+ */
+ @NonNull
+ public Region getCurrentMagnificationRegion() {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(mService).getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getCurrentMagnificationRegion(mDisplayId);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to obtain the current magnified region", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return Region.obtain();
+ }
+
+ /**
* Resets magnification scale and center to their default (e.g. no
* magnification) values.
* <p>
@@ -1531,6 +1573,11 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * <p>
+ * <strong>Note:</strong> This legacy API reset full-screen magnification.
+ * To reset the current controlling magnifier, use
+ * {@link #resetCurrentMagnification(boolean)} ()} instead.
+ * </p>
*
* @param animate {@code true} to animate from the current scale and
* center or {@code false} to reset the scale and center
@@ -1553,6 +1600,36 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Resets magnification scale and center of the controlling magnification
+ * to their default (e.g. no magnification) values.
+ * <p>
+ * <strong>Note:</strong> If the service is not yet connected (e.g.
+ * {@link AccessibilityService#onServiceConnected()} has not yet been
+ * called) or the service has been disconnected, this method will have
+ * no effect and return {@code false}.
+ * </p>
+ *
+ * @param animate {@code true} to animate from the current scale and
+ * center or {@code false} to reset the scale and center
+ * immediately
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public boolean resetCurrentMagnification(boolean animate) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(mService).getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.resetCurrentMagnification(mDisplayId, animate);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to reset", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Sets the {@link MagnificationConfig}. The service controls the magnification by
* setting the config.
* <p>
@@ -1665,6 +1742,10 @@ public abstract class AccessibilityService extends Service {
public interface OnMagnificationChangedListener {
/**
* Called when the magnified region, scale, or center changes.
+ * <p>
+ * <strong>Note:</strong> This legacy callback notifies only full-screen
+ * magnification change.
+ * </p>
*
* @param controller the magnification controller
* @param region the magnification region
@@ -1676,6 +1757,38 @@ public abstract class AccessibilityService extends Service {
*/
void onMagnificationChanged(@NonNull MagnificationController controller,
@NonNull Region region, float scale, float centerX, float centerY);
+
+ /**
+ * Called when the magnified region, mode, scale, or center changes of
+ * all magnification modes.
+ * <p>
+ * <strong>Note:</strong> This method can be overridden to listen to the
+ * magnification changes of all magnification modes then the legacy callback
+ * would not receive the notifications.
+ * Skipping calling super when overriding this method results in
+ * {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)}
+ * not getting called.
+ * </p>
+ *
+ * @param controller the magnification controller
+ * @param region the magnification region
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * it is the region of the screen currently active for magnification.
+ * that is the same region as {@link #getMagnificationRegion()}.
+ * If the config mode is
+ * {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * it is the region of screen projected on the magnification window.
+ * @param config The magnification config. That has the controlling magnification
+ * mode, the new scale and the new screen-relative center position
+ */
+ default void onMagnificationChanged(@NonNull MagnificationController controller,
+ @NonNull Region region, @NonNull MagnificationConfig config) {
+ if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) {
+ onMagnificationChanged(controller, region,
+ config.getScale(), config.getCenterX(), config.getCenterY());
+ }
+ }
}
}
@@ -2449,9 +2562,8 @@ public abstract class AccessibilityService extends Service {
@Override
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
- AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
- centerX, centerY);
+ MagnificationConfig config) {
+ AccessibilityService.this.onMagnificationChanged(displayId, region, config);
}
@Override
@@ -2575,12 +2687,10 @@ public abstract class AccessibilityService extends Service {
/** Magnification changed callbacks for different displays */
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ MagnificationConfig config) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
- args.arg2 = scale;
- args.arg3 = centerX;
- args.arg4 = centerY;
+ args.arg2 = config;
args.argi1 = displayId;
final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
@@ -2739,13 +2849,10 @@ public abstract class AccessibilityService extends Service {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
- final float scale = (float) args.arg2;
- final float centerX = (float) args.arg3;
- final float centerY = (float) args.arg4;
+ final MagnificationConfig config = (MagnificationConfig) args.arg2;
final int displayId = args.argi1;
args.recycle();
- mCallback.onMagnificationChanged(displayId, region, scale,
- centerX, centerY);
+ mCallback.onMagnificationChanged(displayId, region, config);
}
return;
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 651c50f475c6..375383d5d858 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -21,6 +21,7 @@ import android.graphics.Region;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.MagnificationConfig;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -43,7 +44,7 @@ import android.view.MotionEvent;
void onKeyEvent(in KeyEvent event, int sequence);
- void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
+ void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config);
void onMotionEvent(in MotionEvent event);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7d76bbf35081..2cc15b40106b 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -88,8 +88,12 @@ interface IAccessibilityServiceConnection {
Region getMagnificationRegion(int displayId);
+ Region getCurrentMagnificationRegion(int displayId);
+
boolean resetMagnification(int displayId, boolean animate);
+ boolean resetCurrentMagnification(int displayId, boolean animate);
+
boolean setMagnificationConfig(int displayId, in MagnificationConfig config, boolean animate);
void setMagnificationCallbackEnabled(int displayId, boolean enabled);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 2bbf280277ff..fa9de6e27282 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -384,7 +384,7 @@ public class AccountManager {
new PropertyInvalidatedCache<UserIdPackage, Account[]>(
CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
@Override
- protected Account[] recompute(UserIdPackage userAndPackage) {
+ public Account[] recompute(UserIdPackage userAndPackage) {
try {
return mService.getAccountsAsUser(null, userAndPackage.userId, userAndPackage.packageName);
} catch (RemoteException e) {
@@ -392,11 +392,11 @@ public class AccountManager {
}
}
@Override
- protected boolean bypass(UserIdPackage query) {
+ public boolean bypass(UserIdPackage query) {
return query.userId < 0;
}
@Override
- protected boolean debugCompareQueryResults(Account[] l, Account[] r) {
+ public boolean resultEquals(Account[] l, Account[] r) {
if (l == r) {
return true;
} else if (l == null || r == null) {
@@ -455,7 +455,7 @@ public class AccountManager {
new PropertyInvalidatedCache<AccountKeyData, String>(CACHE_USER_DATA_SIZE,
CACHE_KEY_USER_DATA_PROPERTY) {
@Override
- protected String recompute(AccountKeyData accountKeyData) {
+ public String recompute(AccountKeyData accountKeyData) {
Account account = accountKeyData.account;
String key = accountKeyData.key;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1778ea4a32e2..b2c9439d5760 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4645,12 +4645,14 @@ public final class ActivityThread extends ClientTransactionHandler
}
private void handleDumpGfxInfo(DumpComponentInfo info) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
ThreadedRenderer.handleDumpGfxInfo(info.fd.getFileDescriptor(), info.args);
} catch (Exception e) {
Log.w(TAG, "Caught exception from dumpGfxInfo()", e);
} finally {
IoUtils.closeQuietly(info.fd);
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 44fb5db02f5d..49c75c49b2d7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -802,7 +802,7 @@ public class ApplicationPackageManager extends PackageManager {
new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
256, "cache_key.has_system_feature") {
@Override
- protected Boolean recompute(HasSystemFeatureQuery query) {
+ public Boolean recompute(HasSystemFeatureQuery query) {
try {
return ActivityThread.currentActivityThread().getPackageManager().
hasSystemFeature(query.name, query.version);
@@ -1098,7 +1098,7 @@ public class ApplicationPackageManager extends PackageManager {
new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>(
32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
@Override
- protected GetPackagesForUidResult recompute(Integer uid) {
+ public GetPackagesForUidResult recompute(Integer uid) {
try {
return new GetPackagesForUidResult(
ActivityThread.currentActivityThread().
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c895636ddc82..f3e9f105500e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1796,7 +1796,6 @@ class ContextImpl extends Context {
&& ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
flags = flags | Context.RECEIVER_EXPORTED;
}
-
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
@@ -1811,9 +1810,6 @@ class ContextImpl extends Context {
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } catch (WtfException e) {
- Log.wtf(TAG, e.getMessage());
- return null;
}
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ef4d7b1f42e2..978160c1c243 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -505,13 +505,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* block. If this function returns null, the result of the cache query is null. There is no
* "negative cache" in the query: we don't cache null results at all.
*/
- protected abstract Result recompute(Query query);
+ public abstract Result recompute(Query query);
/**
* Return true if the query should bypass the cache. The default behavior is to
* always use the cache but the method can be overridden for a specific class.
*/
- protected boolean bypass(Query query) {
+ public boolean bypass(Query query) {
return false;
}
@@ -519,7 +519,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* Determines if a pair of responses are considered equal. Used to determine whether
* a cache is inadvertently returning stale results when VERIFY is set to true.
*/
- protected boolean debugCompareQueryResults(Result cachedResult, Result fetchedResult) {
+ protected boolean resultEquals(Result cachedResult, Result fetchedResult) {
// If a service crashes and returns a null result, the cached value remains valid.
if (fetchedResult != null) {
return Objects.equals(cachedResult, fetchedResult);
@@ -990,11 +990,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
}
- protected Result maybeCheckConsistency(Query query, Result proposedResult) {
+ private Result maybeCheckConsistency(Query query, Result proposedResult) {
if (VERIFY) {
Result resultToCompare = recompute(query);
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
- if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
+ if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) {
Log.e(TAG, TextUtils.formatSimple(
"cache %s inconsistent for %s is %s should be %s",
cacheName(), queryToString(query),
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 58ded716cf40..00903a880834 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityService.IAccessibilityServiceCl
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1581,7 +1582,7 @@ public final class UiAutomation {
@Override
public void onMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ MagnificationConfig config) {
/* do nothing */
}
diff --git a/core/java/android/app/WtfException.java b/core/java/android/app/WtfException.java
deleted file mode 100644
index ba8dbefad160..000000000000
--- a/core/java/android/app/WtfException.java
+++ /dev/null
@@ -1,66 +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.app;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Exception meant to be thrown instead of calling Log.wtf() such that server side code can
- * throw this exception, and it will carry across the binder to do client side logging.
- * {@hide}
- */
-public final class WtfException extends RuntimeException implements Parcelable {
- public static final @android.annotation.NonNull
- Creator<WtfException> CREATOR = new Creator<WtfException>() {
- @Override
- public WtfException createFromParcel(Parcel source) {
- return new WtfException(source.readString8());
- }
-
- @Override
- public WtfException[] newArray(int size) {
- return new WtfException[size];
- }
- };
-
- public WtfException(@android.annotation.NonNull String message) {
- super(message);
- }
-
- /** {@hide} */
- public static Throwable readFromParcel(Parcel in) {
- final String msg = in.readString8();
- return new WtfException(msg);
- }
-
- /** {@hide} */
- public static void writeToParcel(Parcel out, Throwable t) {
- out.writeString8(t.getMessage());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
- dest.writeString8(getMessage());
- }
-}
-
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index 3d0bf91bf8d7..acd404b7d475 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -84,7 +84,7 @@ public final class ChangeIdStateCache
}
@Override
- protected Boolean recompute(ChangeIdStateQuery query) {
+ public Boolean recompute(ChangeIdStateQuery query) {
final long token = Binder.clearCallingIdentity();
try {
if (query.type == ChangeIdStateQuery.QUERY_BY_PACKAGE_NAME) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9a0f02e8b2b6..856a8e1f8199 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1062,7 +1062,7 @@ public final class BluetoothAdapter {
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Void query) {
+ public Integer recompute(Void query) {
try {
return mService.getState();
} catch (RemoteException e) {
@@ -2085,7 +2085,7 @@ public final class BluetoothAdapter {
8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
@@ -2540,7 +2540,7 @@ public final class BluetoothAdapter {
*/
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Void query) {
+ public Integer recompute(Void query) {
try {
return mService.getAdapterConnectionState();
} catch (RemoteException e) {
@@ -2605,7 +2605,7 @@ public final class BluetoothAdapter {
8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(Integer query) {
+ public Integer recompute(Integer query) {
try {
mServiceLock.readLock().lock();
if (mService != null) {
@@ -3064,9 +3064,6 @@ public final class BluetoothAdapter {
BluetoothCsipSetCoordinator csipSetCoordinator =
new BluetoothCsipSetCoordinator(context, listener, this);
return true;
- } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
- BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
- return true;
} else {
return false;
}
@@ -3169,10 +3166,6 @@ public final class BluetoothAdapter {
(BluetoothCsipSetCoordinator) proxy;
csipSetCoordinator.close();
break;
- case BluetoothProfile.LE_CALL_CONTROL:
- BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
- tbs.close();
- break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 69525b543478..a3c45d0276ca 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -120,6 +120,7 @@ public final class BluetoothClass implements Parcelable {
private static final int BITMASK = 0xFFE000;
public static final int LIMITED_DISCOVERABILITY = 0x002000;
+ public static final int LE_AUDIO = 0x004000;
public static final int POSITIONING = 0x010000;
public static final int NETWORKING = 0x020000;
public static final int RENDER = 0x040000;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 93f026860856..fc99942cb784 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1604,7 +1604,7 @@ public final class BluetoothDevice implements Parcelable, Attributable {
8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
- protected Integer recompute(BluetoothDevice query) {
+ public Integer recompute(BluetoothDevice query) {
try {
return sService.getBondState(query, mAttributionSource);
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
new file mode 100644
index 000000000000..b866cce22470
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.bluetooth.le.ScanResult;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is
+ * offloaded to a Broadcast Assistant.
+ *
+ * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast
+ * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This
+ * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is
+ * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast
+ * Assistant.
+ *
+ * <p>Once a GATT connection is established between the BASS client and the BASS server, the
+ * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast
+ * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the
+ * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the
+ * Assistant about changes such as addition and removal of Broadcast Sources.
+ *
+ * @hide
+ */
+public abstract class BluetoothLeBroadcastAssistantCallback {
+
+ /**
+ * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server
+ *
+ * @hide
+ */
+ @IntDef(
+ prefix = "BASS_STATUS_",
+ value = {
+ BASS_STATUS_SUCCESS,
+ BASS_STATUS_FAILURE,
+ BASS_STATUS_INVALID_GATT_HANDLE,
+ BASS_STATUS_TXN_TIMEOUT,
+ BASS_STATUS_INVALID_SOURCE_ID,
+ BASS_STATUS_COLOCATED_SRC_UNAVAILABLE,
+ BASS_STATUS_INVALID_SOURCE_SELECTED,
+ BASS_STATUS_SOURCE_UNAVAILABLE,
+ BASS_STATUS_DUPLICATE_ADDITION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BassStatus {}
+
+ public static final int BASS_STATUS_SUCCESS = 0x00;
+ public static final int BASS_STATUS_FAILURE = 0x01;
+ public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02;
+ public static final int BASS_STATUS_TXN_TIMEOUT = 0x03;
+
+ public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04;
+ public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05;
+ public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06;
+ public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07;
+ public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08;
+ public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09;
+ public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10;
+
+ /**
+ * Callback invoked when a new LE Audio Broadcast Source is found.
+ *
+ * @param result {@link ScanResult} scan result representing a Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {}
+
+ /**
+ * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs)
+ * of an LE Audio Broadcast Source.
+ *
+ * @param source the selected Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceSelected(
+ @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
+
+ /**
+ * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio
+ * Broadcast Source.
+ *
+ * @param source the Broadcast Source with which synchronization was lost
+ */
+ public void onBluetoothLeBroadcastSourceLost(
+ @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {}
+
+ /**
+ * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan
+ * Delegator (within a Broadcast Sink, for example).
+ *
+ * @param sink Scan Delegator device on which a new Broadcast Source has been added
+ * @param source the added Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceAdded(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+
+ /**
+ * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator
+ * has been updated.
+ *
+ * @param sink Scan Delegator device on which a Broadcast Source has been updated
+ * @param source the updated Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceUpdated(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+
+ /**
+ * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the
+ * Scan Delegator (within a Broadcast Sink, for example).
+ *
+ * @param sink Scan Delegator device from which a Broadcast Source has been removed
+ * @param source the removed Broadcast Source
+ */
+ public void onBluetoothLeBroadcastSourceRemoved(
+ @NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastSourceInfo source,
+ @BassStatus int status) {}
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
deleted file mode 100644
index fb7789db25c7..000000000000
--- a/core/java/android/bluetooth/BluetoothLeCall.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2021 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.ParcelUuid;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Representation of Call
- *
- * @hide
- */
-public final class BluetoothLeCall implements Parcelable {
-
- /** @hide */
- @IntDef(prefix = "STATE_", value = {
- STATE_INCOMING,
- STATE_DIALING,
- STATE_ALERTING,
- STATE_ACTIVE,
- STATE_LOCALLY_HELD,
- STATE_REMOTELY_HELD,
- STATE_LOCALLY_AND_REMOTELY_HELD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface State {
- }
-
- /**
- * A remote party is calling (incoming call).
- *
- * @hide
- */
- public static final int STATE_INCOMING = 0x00;
-
- /**
- * The process to call the remote party has started but the remote party is not
- * being alerted (outgoing call).
- *
- * @hide
- */
- public static final int STATE_DIALING = 0x01;
-
- /**
- * A remote party is being alerted (outgoing call).
- *
- * @hide
- */
- public static final int STATE_ALERTING = 0x02;
-
- /**
- * The call is in an active conversation.
- *
- * @hide
- */
- public static final int STATE_ACTIVE = 0x03;
-
- /**
- * The call is connected but held locally. “Locally Held” implies that either
- * the server or the client can affect the state.
- *
- * @hide
- */
- public static final int STATE_LOCALLY_HELD = 0x04;
-
- /**
- * The call is connected but held remotely. “Remotely Held” means that the state
- * is controlled by the remote party of a call.
- *
- * @hide
- */
- public static final int STATE_REMOTELY_HELD = 0x05;
-
- /**
- * The call is connected but held both locally and remotely.
- *
- * @hide
- */
- public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
-
- /**
- * Whether the call direction is outgoing.
- *
- * @hide
- */
- public static final int FLAG_OUTGOING_CALL = 0x00000001;
-
- /**
- * Whether the call URI and Friendly Name are withheld by server.
- *
- * @hide
- */
- public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
-
- /**
- * Whether the call URI and Friendly Name are withheld by network.
- *
- * @hide
- */
- public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
-
- /** Unique UUID that identifies this call */
- private UUID mUuid;
-
- /** Remote Caller URI */
- private String mUri;
-
- /** Caller friendly name */
- private String mFriendlyName;
-
- /** Call state */
- private @State int mState;
-
- /** Call flags */
- private int mCallFlags;
-
- /** @hide */
- public BluetoothLeCall(@NonNull BluetoothLeCall that) {
- mUuid = new UUID(that.getUuid().getMostSignificantBits(),
- that.getUuid().getLeastSignificantBits());
- mUri = that.mUri;
- mFriendlyName = that.mFriendlyName;
- mState = that.mState;
- mCallFlags = that.mCallFlags;
- }
-
- /** @hide */
- public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
- @State int state, int callFlags) {
- mUuid = uuid;
- mUri = uri;
- mFriendlyName = friendlyName;
- mState = state;
- mCallFlags = callFlags;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- BluetoothLeCall that = (BluetoothLeCall) o;
- return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
- && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
- && mCallFlags == that.mCallFlags;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
- }
-
- /**
- * Returns a string representation of this BluetoothLeCall.
- *
- * <p>
- * Currently this is the UUID.
- *
- * @return string representation of this BluetoothLeCall
- */
- @Override
- public String toString() {
- return mUuid.toString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeParcelable(new ParcelUuid(mUuid), 0);
- out.writeString(mUri);
- out.writeString(mFriendlyName);
- out.writeInt(mState);
- out.writeInt(mCallFlags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
- new Parcelable.Creator<BluetoothLeCall>() {
- public BluetoothLeCall createFromParcel(Parcel in) {
- return new BluetoothLeCall(in);
- }
-
- public BluetoothLeCall[] newArray(int size) {
- return new BluetoothLeCall[size];
- }
- };
-
- private BluetoothLeCall(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
- mUri = in.readString();
- mFriendlyName = in.readString();
- mState = in.readInt();
- mCallFlags = in.readInt();
- }
-
- /**
- * Returns an UUID of this BluetoothLeCall.
- *
- * <p>
- * An UUID is unique identifier of a BluetoothLeCall.
- *
- * @return UUID of this BluetoothLeCall
- * @hide
- */
- public @NonNull UUID getUuid() {
- return mUuid;
- }
-
- /**
- * Returns a URI of the remote party of this BluetoothLeCall.
- *
- * @return string representation of this BluetoothLeCall
- * @hide
- */
- public @NonNull String getUri() {
- return mUri;
- }
-
- /**
- * Returns a friendly name of the call.
- *
- * @return friendly name representation of this BluetoothLeCall
- * @hide
- */
- public @NonNull String getFriendlyName() {
- return mFriendlyName;
- }
-
- /**
- * Returns the call state.
- *
- * @return the state of this BluetoothLeCall
- * @hide
- */
- public @State int getState() {
- return mState;
- }
-
- /**
- * Returns the call flags.
- *
- * @return call flags
- * @hide
- */
- public int getCallFlags() {
- return mCallFlags;
- }
-
- /**
- * Whether the call direction is incoming.
- *
- * @return true if incoming call, false otherwise
- * @hide
- */
- public boolean isIncomingCall() {
- return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
deleted file mode 100644
index 5283e0804252..000000000000
--- a/core/java/android/bluetooth/BluetoothLeCallControl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright 2019 HIMSA II K/S - www.himsa.com.
- * Represented by EHIMA - www.ehima.com
- *
- * 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.bluetooth;
-
-import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-import android.annotation.SuppressLint;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides the APIs to control the Call Control profile.
- *
- * <p>
- * This class provides Bluetooth Telephone Bearer Service functionality,
- * allowing applications to expose a GATT Service based interface to control the
- * state of the calls by remote devices such as LE audio devices.
- *
- * <p>
- * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
- * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothLeCallControl proxy object.
- *
- * @hide
- */
-public final class BluetoothLeCallControl implements BluetoothProfile {
- private static final String TAG = "BluetoothLeCallControl";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- /** @hide */
- @IntDef(prefix = "RESULT_", value = {
- RESULT_SUCCESS,
- RESULT_ERROR_UNKNOWN_CALL_ID,
- RESULT_ERROR_INVALID_URI,
- RESULT_ERROR_APPLICATION
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Result {
- }
-
- /**
- * Opcode write was successful.
- *
- * @hide
- */
- public static final int RESULT_SUCCESS = 0;
-
- /**
- * Unknown call Id has been used in the operation.
- *
- * @hide
- */
- public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
-
- /**
- * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
- *
- * @hide
- */
- public static final int RESULT_ERROR_INVALID_URI = 2;
-
- /**
- * Application internal error.
- *
- * @hide
- */
- public static final int RESULT_ERROR_APPLICATION = 3;
-
- /** @hide */
- @IntDef(prefix = "TERMINATION_REASON_", value = {
- TERMINATION_REASON_INVALID_URI,
- TERMINATION_REASON_FAIL,
- TERMINATION_REASON_REMOTE_HANGUP,
- TERMINATION_REASON_SERVER_HANGUP,
- TERMINATION_REASON_LINE_BUSY,
- TERMINATION_REASON_NETWORK_CONGESTION,
- TERMINATION_REASON_CLIENT_HANGUP,
- TERMINATION_REASON_NO_SERVICE,
- TERMINATION_REASON_NO_ANSWER
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TerminationReason {
- }
-
- /**
- * Remote Caller ID value used to place a call was formed improperly.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_INVALID_URI = 0x00;
-
- /**
- * Call fail.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_FAIL = 0x01;
-
- /**
- * Remote party ended call.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
-
- /**
- * Call ended from the server.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
-
- /**
- * Line busy.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
-
- /**
- * Network congestion.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
-
- /**
- * Client terminated.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
-
- /**
- * No service.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
-
- /**
- * No answer.
- *
- * @hide
- */
- public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
-
- /*
- * Flag indicating support for hold/unhold call feature.
- *
- * @hide
- */
- public static final int CAPABILITY_HOLD_CALL = 0x00000001;
-
- /**
- * Flag indicating support for joining calls feature.
- *
- * @hide
- */
- public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
-
- private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
- private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
-
- private static final int REG_TIMEOUT = 10000;
-
- /**
- * The template class is used to call callback functions on events from the TBS
- * server. Callback functions are wrapped in this class and registered to the
- * Android system during app registration.
- *
- * @hide
- */
- public abstract static class Callback {
-
- private static final String TAG = "BluetoothLeCallControl.Callback";
-
- /**
- * Called when a remote client requested to accept the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to be accepted
- * @hide
- */
- public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
-
- /**
- * A remote client has requested to terminate the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to terminate
- * @hide
- */
- public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
-
- /**
- * A remote client has requested to hold the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to be put on hold
- * @hide
- */
- public void onHoldCall(int requestId, @NonNull UUID callId) {
- Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
- }
-
- /**
- * A remote client has requested to unhold the call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The call Id requested to unhold
- * @hide
- */
- public void onUnholdCall(int requestId, @NonNull UUID callId) {
- Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
- }
-
- /**
- * A remote client has requested to place a call.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callId The Id to be assigned for the new call
- * @param uri The caller URI requested
- * @hide
- */
- public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
-
- /**
- * A remote client has requested to join the calls.
- *
- * <p>
- * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
- * request.
- *
- * @param requestId The Id of the request
- * @param callIds The call Id list requested to join
- * @hide
- */
- public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
- Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
- }
- }
-
- private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
-
- private final Executor mExecutor;
- private final Callback mCallback;
-
- CallbackWrapper(Executor executor, Callback callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onBearerRegistered(int ccid) {
- synchronized (mServerIfLock) {
- if (mCallback != null) {
- mCcid = ccid;
- mServerIfLock.notifyAll();
- } else {
- // registration timeout
- Log.e(TAG, "onBearerRegistered: mCallback is null");
- }
- }
- }
-
- @Override
- public void onAcceptCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onTerminateCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onHoldCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onUnholdCall(int requestId, ParcelUuid uuid) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
-
- @Override
- public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
- List<UUID> uuids = new ArrayList<>();
- for (ParcelUuid parcelUuid : parcelUuids) {
- uuids.add(parcelUuid.getUuid());
- }
-
- final long identityToken = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
- };
-
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothLeCallControl mService;
- private BluetoothAdapter mAdapter;
- private int mCcid = 0;
- private String mToken;
- private Callback mCallback = null;
- private Object mServerIfLock = new Object();
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG)
- Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- doUnbind();
- } else {
- doBind();
- }
- }
- };
-
- /**
- * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
- * telephone bearer service.
- */
- /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
- mContext = context;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mServiceListener = listener;
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- private boolean doBind() {
- synchronized (mConnection) {
- if (mService == null) {
- if (VDBG)
- Log.d(TAG, "Binding service...");
- try {
- return mAdapter.getBluetoothManager().
- bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
- mConnection);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to bind TelephoneBearerService", e);
- }
- }
- }
- return false;
- }
-
- private void doUnbind() {
- synchronized (mConnection) {
- if (mService != null) {
- if (VDBG)
- Log.d(TAG, "Unbinding service...");
- try {
- mAdapter.getBluetoothManager().
- unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
- mConnection);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
- } finally {
- mService = null;
- }
- }
- }
- }
-
- /* package */ void close() {
- if (VDBG)
- log("close()");
- unregisterBearer();
-
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "", re);
- }
- }
- mServiceListener = null;
- doUnbind();
- }
-
- private IBluetoothLeCallControl getService() {
- return mService;
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public int getConnectionState(@Nullable BluetoothDevice device) {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public @NonNull List<BluetoothDevice> getConnectedDevices() {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Not supported
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
- @NonNull int[] states) {
- throw new UnsupportedOperationException("not supported");
- }
-
- /**
- * Register Telephone Bearer exposing the interface that allows remote devices
- * to track and control the call states.
- *
- * <p>
- * This is an asynchronous call. The callback is used to notify success or
- * failure if the function returns true.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * <!-- The UCI is a String identifier of the telephone bearer as defined at
- * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
- * (login required). -->
- *
- * <!-- The examples of common URI schemes can be found in
- * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
- *
- * <!-- The Technology is an integer value. The possible values are defined at
- * https://www.bluetooth.com/specifications/assigned-numbers (login required).
- * -->
- *
- * @param uci Bearer Unique Client Identifier
- * @param uriSchemes URI Schemes supported list
- * @param capabilities bearer capabilities
- * @param provider Network provider name
- * @param technology Network technology
- * @param executor {@link Executor} object on which callback will be
- * executed. The Executor object is required.
- * @param callback {@link Callback} object to which callback messages will
- * be sent. The Callback object is required.
- * @return true on success, false otherwise
- * @hide
- */
- @SuppressLint("ExecutorRegistration")
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean registerBearer(@Nullable String uci,
- @NonNull List<String> uriSchemes, int capabilities,
- @NonNull String provider, int technology,
- @NonNull Executor executor, @NonNull Callback callback) {
- if (DBG) {
- Log.d(TAG, "registerBearer");
- }
- if (callback == null) {
- throw new IllegalArgumentException("null parameter: " + callback);
- }
- if (mCcid != 0) {
- return false;
- }
-
- mToken = uci;
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- synchronized (mServerIfLock) {
- if (mCallback != null) {
- Log.e(TAG, "Bearer can be opened only once");
- return false;
- }
-
- mCallback = callback;
- try {
- CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
- service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
- provider, technology);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- mCallback = null;
- return false;
- }
-
- try {
- mServerIfLock.wait(REG_TIMEOUT);
- } catch (InterruptedException e) {
- Log.e(TAG, "" + e);
- mCallback = null;
- }
-
- if (mCcid == 0) {
- mCallback = null;
- return false;
- }
-
- return true;
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
-
- return false;
- }
-
- /**
- * Unregister Telephone Bearer Service and destroy all the associated data.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void unregisterBearer() {
- if (DBG) {
- Log.d(TAG, "unregisterBearer");
- }
- if (mCcid == 0) {
- return;
- }
-
- int ccid = mCcid;
- mCcid = 0;
- mCallback = null;
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.unregisterBearer(mToken);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Get the Content Control ID (CCID) value.
- *
- * @return ccid Content Control ID value
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public int getContentControlId() {
- return mCcid;
- }
-
- /**
- * Notify about the newly added call.
- *
- * <p>
- * This shall be called as early as possible after the call has been added.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param call Newly added call
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallAdded(@NonNull BluetoothLeCall call) {
- if (DBG) {
- Log.d(TAG, "onCallAdded: call=" + call);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callAdded(mCcid, call);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Notify about the removed call.
- *
- * <p>
- * This shall be called as early as possible after the call has been removed.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callId The Id of a call that has been removed
- * @param reason Call termination reason
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
- if (DBG) {
- Log.d(TAG, "callRemoved: callId=" + callId);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callRemoved(mCcid, new ParcelUuid(callId), reason);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Notify the call state change
- *
- * <p>
- * This shall be called as early as possible after the state of the call has
- * changed.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callId The call Id that state has been changed
- * @param state Call state
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
- if (DBG) {
- Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.callStateChanged(mCcid, new ParcelUuid(callId), state);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Provide the current calls list
- *
- * <p>
- * This function must be invoked after registration if application has any
- * calls.
- *
- * @param calls current calls list
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.currentCallsList(mCcid, calls);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- }
-
- /**
- * Provide the network current status
- *
- * <p>
- * This function must be invoked on change of network state.
- *
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * <!-- The Technology is an integer value. The possible values are defined at
- * https://www.bluetooth.com/specifications/assigned-numbers (login required).
- * -->
- *
- * @param provider Network provider name
- * @param technology Network technology
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void networkStateChanged(@NonNull String provider, int technology) {
- if (DBG) {
- Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.networkStateChanged(mCcid, provider, technology);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- }
-
- /**
- * Send a response to a call control request to a remote device.
- *
- * <p>
- * This function must be invoked in when a request is received by one of these
- * callback methods:
- *
- * <ul>
- * <li>{@link Callback#onAcceptCall}
- * <li>{@link Callback#onTerminateCall}
- * <li>{@link Callback#onHoldCall}
- * <li>{@link Callback#onUnholdCall}
- * <li>{@link Callback#onPlaceCall}
- * <li>{@link Callback#onJoinCalls}
- * </ul>
- *
- * @param requestId The ID of the request that was received with the callback
- * @param result The result of the request to be sent to the remote devices
- */
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void requestResult(int requestId, @Result int result) {
- if (DBG) {
- Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
- }
- if (mCcid == 0) {
- return;
- }
-
- final IBluetoothLeCallControl service = getService();
- if (service != null) {
- try {
- service.requestResult(mCcid, requestId, result);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
- }
-
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- private static boolean isValidDevice(@Nullable BluetoothDevice device) {
- return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-
- private final IBluetoothProfileServiceConnection mConnection =
- new IBluetoothProfileServiceConnection.Stub() {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) {
- Log.d(TAG, "Proxy object connected");
- }
- mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) {
- Log.d(TAG, "Proxy object disconnected");
- }
- doUnbind();
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
- }
- };
-
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_TBS_SERVICE_CONNECTED: {
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
- BluetoothLeCallControl.this);
- }
- break;
- }
- case MESSAGE_TBS_SERVICE_DISCONNECTED: {
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
- }
- break;
- }
- }
- }
- };
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index d0f74e985729..e047e5d81a9d 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,19 +240,12 @@ public interface BluetoothProfile {
int LE_AUDIO_BROADCAST = 26;
/**
- * @hide
- * Telephone Bearer Service from Call Control Profile
- *
- */
- int LE_CALL_CONTROL = 27;
-
- /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 27;
+ int MAX_PROFILE_ID = 26;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 01d231c51751..7b9d37e7136d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2670,6 +2670,9 @@ public abstract class ContentResolver implements ContentInterface {
* {@link ContentObserver#onChange(boolean, Collection, int, UserHandle)} should be
* overwritten to get the corresponding {@link UserHandle} for that notification.
*
+ * <p> If you don't hold the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission, you can register the {@link ContentObserver} only for current user.
+ *
* @param uri The URI to watch for changes. This can be a specific row URI,
* or a base URI for a whole class of content.
* @param notifyForDescendants When false, the observer will be notified
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 338dfd62f316..819cbb0f347d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -10187,16 +10187,15 @@ public abstract class PackageManager {
16, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getApplicationInfo") {
@Override
- protected ApplicationInfo recompute(ApplicationInfoQuery query) {
+ public ApplicationInfo recompute(ApplicationInfoQuery query) {
return getApplicationInfoAsUserUncached(
query.packageName, query.flags, query.userId);
}
@Override
- protected ApplicationInfo maybeCheckConsistency(
- ApplicationInfoQuery query, ApplicationInfo proposedResult) {
+ public boolean resultEquals(ApplicationInfo cached, ApplicationInfo fetched) {
// Implementing this debug check for ApplicationInfo would require a
// complicated deep comparison, so just bypass it for now.
- return proposedResult;
+ return true;
}
};
@@ -10289,16 +10288,15 @@ public abstract class PackageManager {
32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getPackageInfo") {
@Override
- protected PackageInfo recompute(PackageInfoQuery query) {
+ public PackageInfo recompute(PackageInfoQuery query) {
return getPackageInfoAsUserUncached(
query.packageName, query.flags, query.userId);
}
@Override
- protected PackageInfo maybeCheckConsistency(
- PackageInfoQuery query, PackageInfo proposedResult) {
+ public boolean resultEquals(PackageInfo cached, PackageInfo fetched) {
// Implementing this debug check for PackageInfo would require a
// complicated deep comparison, so just bypass it for now.
- return proposedResult;
+ return true;
}
};
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index dbd3d5c4a7e7..23cae4c04467 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -1152,7 +1152,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
*/
@Nullable
private ArrayMap<String, String> buildAppClassNamesByProcess() {
- if (processes == null) {
+ if (ArrayUtils.size(processes) == 0) {
return null;
}
final ArrayMap<String, String> ret = new ArrayMap<>(4);
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index fd1a33161740..6c83057fdf29 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -78,7 +78,7 @@ public class SurfaceUtils {
public static boolean isSurfaceForHwVideoEncoder(Surface surface) {
checkNotNull(surface);
long usageFlags = nativeDetectSurfaceUsageFlags(surface);
- long disallowedFlags = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | USAGE_HW_COMPOSER
+ long disallowedFlags = USAGE_HW_COMPOSER
| USAGE_RENDERSCRIPT | HardwareBuffer.USAGE_CPU_READ_OFTEN;
long allowedFlags = HardwareBuffer.USAGE_VIDEO_ENCODE;
boolean videoEncoderConsumer = ((usageFlags & disallowedFlags) == 0
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 00374644d72c..de5c9adbc599 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1206,6 +1206,17 @@ public final class DisplayManager {
}
/**
+ * Returns whether the specified display supports DISPLAY_DECORATION.
+ *
+ * @param displayId The display to query support.
+ *
+ * @hide
+ */
+ public boolean getDisplayDecorationSupport(int displayId) {
+ return mGlobal.getDisplayDecorationSupport(displayId);
+ }
+
+ /**
* Returns the user preference for "Match content frame rate".
* <p>
* Never: Even if the app requests it, the device will never try to match its output to the
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index e73116556758..bf6e6651741c 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -128,7 +128,7 @@ public final class DisplayManagerGlobal {
8, // size of display cache
CACHE_KEY_DISPLAY_INFO_PROPERTY) {
@Override
- protected DisplayInfo recompute(Integer id) {
+ public DisplayInfo recompute(Integer id) {
try {
return mDm.getDisplayInfo(id);
} catch (RemoteException ex) {
@@ -812,6 +812,21 @@ public final class DisplayManagerGlobal {
}
/**
+ * Report whether the display supports DISPLAY_DECORATION.
+ *
+ * @param displayId The display whose support is being queried.
+ *
+ * @hide
+ */
+ public boolean getDisplayDecorationSupport(int displayId) {
+ try {
+ return mDm.getDisplayDecorationSupport(displayId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the brightness of the display.
*
* @param displayId The display from which to get the brightness
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 82b31d48d5fe..d38d388ca8a3 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -180,4 +180,7 @@ interface IDisplayManager {
// Returns the refresh rate switching type.
int getRefreshRateSwitchingType();
+
+ // Query for DISPLAY_DECORATION support.
+ boolean getDisplayDecorationSupport(int displayId);
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 56f81423db4e..b97055976e3e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -306,22 +306,21 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
- mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures, previewSurface,
- debugConsent);
+ final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
+ previewSurface, debugConsent);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -359,21 +358,20 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollRemotely is already canceled.");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollRemotely is already canceled.");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enrollRemotely");
- mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures);
+ final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
+ mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
@@ -713,10 +711,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) {
try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1100,9 +1098,16 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index e9198246dee3..989b001ca8bf 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -76,15 +76,16 @@ interface IFaceService {
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start face enrollment
- void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
- String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent);
+ long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ String opPackageName, in int [] disabledFeatures,
+ in Surface previewSurface, boolean debugConsent);
// Start remote face enrollment
- void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
+ long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Removes the specified face enrollment for the specified userId.
void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe04e5d35784..acf9427b1241 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -183,9 +183,16 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
private class OnEnrollCancelListener implements OnCancelListener {
+ private final long mAuthRequestId;
+
+ private OnEnrollCancelListener(long id) {
+ mAuthRequestId = id;
+ }
+
@Override
public void onCancel() {
- cancelEnrollment();
+ Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId);
+ cancelEnrollment(mAuthRequestId);
}
}
@@ -646,20 +653,19 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
throw new IllegalArgumentException("Must supply an enrollment callback");
}
- if (cancel != null) {
- if (cancel.isCanceled()) {
- Slog.w(TAG, "enrollment already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnEnrollCancelListener());
- }
+ if (cancel != null && cancel.isCanceled()) {
+ Slog.w(TAG, "enrollment already canceled");
+ return;
}
if (mService != null) {
try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), enrollReason);
+ final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
+ mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+ if (cancel != null) {
+ cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -1302,9 +1308,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
return allSensors.isEmpty() ? null : allSensors.get(0);
}
- private void cancelEnrollment() {
+ private void cancelEnrollment(long requestId) {
if (mService != null) try {
- mService.cancelEnrollment(mToken);
+ mService.cancelEnrollment(mToken, requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ba1dc6da62a6..cbff8b11a72a 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -84,11 +84,11 @@ interface IFingerprintService {
void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId);
// Start fingerprint enrollment
- void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+ long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
String opPackageName, int enrollReason);
// Cancel enrollment in progress
- void cancelEnrollment(IBinder token);
+ void cancelEnrollment(IBinder token, long requestId);
// Any errors resulting from this call will be returned to the listener
void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6253fb90323a..ef349a96ee17 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -215,14 +215,6 @@ public final class InputManager {
public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;
/**
- * Check whether apps are using FLAG_SLIPPERY for their windows. We expect that this flag is
- * only used by the system components. If so, we can lock it down.
- * @hide
- */
- @ChangeId
- public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
-
- /**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index b3f734524078..1ac3f0a6d7d1 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -39,7 +39,7 @@ import java.util.Set;
// TODO: Add documents
/** @hide */
-public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
@NonNull private final Set<String> mAllowedNetworkPlmnIds;
private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
@@ -51,7 +51,7 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
private final boolean mRequireOpportunistic;
- private VcnCellUnderlyingNetworkPriority(
+ private VcnCellUnderlyingNetworkTemplate(
int networkQuality,
boolean allowMetered,
Set<String> allowedNetworkPlmnIds,
@@ -92,7 +92,7 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnCellUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnCellUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
@@ -117,7 +117,7 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
- return new VcnCellUnderlyingNetworkPriority(
+ return new VcnCellUnderlyingNetworkTemplate(
networkQuality,
allowMetered,
allowedNetworkPlmnIds,
@@ -190,11 +190,11 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
return false;
}
- if (!(other instanceof VcnCellUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnCellUnderlyingNetworkTemplate)) {
return false;
}
- final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other;
+ final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
&& Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
&& mAllowRoaming == rhs.mAllowRoaming
@@ -211,7 +211,7 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
}
/** This class is used to incrementally build WifiNetworkPriority objects. */
- public static final class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
@@ -280,10 +280,10 @@ public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetwork
return this;
}
- /** Build the VcnCellUnderlyingNetworkPriority. */
+ /** Build the VcnCellUnderlyingNetworkTemplate. */
@NonNull
- public VcnCellUnderlyingNetworkPriority build() {
- return new VcnCellUnderlyingNetworkPriority(
+ public VcnCellUnderlyingNetworkTemplate build() {
+ return new VcnCellUnderlyingNetworkTemplate(
mNetworkQuality,
mAllowMetered,
mAllowedNetworkPlmnIds,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 55d3ecd2c92f..d07c24a6529c 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,7 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -162,12 +162,12 @@ public final class VcnGatewayConnectionConfig {
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final LinkedHashSet<VcnUnderlyingNetworkPriority>
+ public static final LinkedHashSet<VcnUnderlyingNetworkTemplate>
DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
static {
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnCellUnderlyingNetworkPriority.Builder()
+ new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */)
@@ -175,13 +175,13 @@ public final class VcnGatewayConnectionConfig {
.build());
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.build());
DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
- new VcnCellUnderlyingNetworkPriority.Builder()
+ new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */)
@@ -202,7 +202,7 @@ public final class VcnGatewayConnectionConfig {
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
- @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
+ @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities;
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -215,7 +215,7 @@ public final class VcnGatewayConnectionConfig {
@NonNull String gatewayConnectionName,
@NonNull IkeTunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
- @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
@@ -265,7 +265,7 @@ public final class VcnGatewayConnectionConfig {
new LinkedHashSet<>(
PersistableBundleUtils.toList(
networkPrioritiesBundle,
- VcnUnderlyingNetworkPriority::fromPersistableBundle));
+ VcnUnderlyingNetworkTemplate::fromPersistableBundle));
}
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -368,14 +368,14 @@ public final class VcnGatewayConnectionConfig {
}
/**
- * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not
+ * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not
* configured.
*
- * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>)
+ * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>)
* @hide
*/
@NonNull
- public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() {
+ public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
}
@@ -418,7 +418,7 @@ public final class VcnGatewayConnectionConfig {
final PersistableBundle networkPrioritiesBundle =
PersistableBundleUtils.fromList(
new ArrayList<>(mUnderlyingNetworkPriorities),
- VcnUnderlyingNetworkPriority::toPersistableBundle);
+ VcnUnderlyingNetworkTemplate::toPersistableBundle);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
@@ -465,7 +465,7 @@ public final class VcnGatewayConnectionConfig {
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull
- private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities =
+ private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities =
new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -539,7 +539,7 @@ public final class VcnGatewayConnectionConfig {
}
/**
- * Set the VcnUnderlyingNetworkPriority list.
+ * Set the VcnUnderlyingNetworkTemplate list.
*
* @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
* are ordered from most to least preferred, or an empty list to use the default
@@ -550,7 +550,7 @@ public final class VcnGatewayConnectionConfig {
/** @hide */
@NonNull
public Builder setVcnUnderlyingNetworkPriorities(
- @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) {
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) {
Objects.requireNonNull(
mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 551f75772b9a..d306d5cb6826 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -33,7 +33,7 @@ import java.util.Objects;
// TODO: Add documents
/** @hide */
-public abstract class VcnUnderlyingNetworkPriority {
+public abstract class VcnUnderlyingNetworkTemplate {
/** @hide */
protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
/** @hide */
@@ -68,7 +68,7 @@ public abstract class VcnUnderlyingNetworkPriority {
private final boolean mAllowMetered;
/** @hide */
- protected VcnUnderlyingNetworkPriority(
+ protected VcnUnderlyingNetworkTemplate(
int networkPriorityType, int networkQuality, boolean allowMetered) {
mNetworkPriorityType = networkPriorityType;
mNetworkQuality = networkQuality;
@@ -89,16 +89,16 @@ public abstract class VcnUnderlyingNetworkPriority {
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY);
switch (networkPriorityType) {
case NETWORK_PRIORITY_TYPE_WIFI:
- return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in);
+ return VcnWifiUnderlyingNetworkTemplate.fromPersistableBundle(in);
case NETWORK_PRIORITY_TYPE_CELL:
- return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in);
+ return VcnCellUnderlyingNetworkTemplate.fromPersistableBundle(in);
default:
throw new IllegalArgumentException(
"Invalid networkPriorityType:" + networkPriorityType);
@@ -124,11 +124,11 @@ public abstract class VcnUnderlyingNetworkPriority {
@Override
public boolean equals(@Nullable Object other) {
- if (!(other instanceof VcnUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnUnderlyingNetworkTemplate)) {
return false;
}
- final VcnUnderlyingNetworkPriority rhs = (VcnUnderlyingNetworkPriority) other;
+ final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
return mNetworkPriorityType == rhs.mNetworkPriorityType
&& mNetworkQuality == rhs.mNetworkQuality
&& mAllowMetered == rhs.mAllowMetered;
@@ -168,7 +168,7 @@ public abstract class VcnUnderlyingNetworkPriority {
}
/**
- * This class is used to incrementally build VcnUnderlyingNetworkPriority objects.
+ * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects.
*
* @param <T> The subclass to be built.
*/
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 85eb100779a2..6bbb2bfecda4 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -28,11 +28,11 @@ import java.util.Objects;
// TODO: Add documents
/** @hide */
-public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String SSID_KEY = "mSsid";
@Nullable private final String mSsid;
- private VcnWifiUnderlyingNetworkPriority(
+ private VcnWifiUnderlyingNetworkTemplate(
int networkQuality, boolean allowMetered, String ssid) {
super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
mSsid = ssid;
@@ -43,14 +43,14 @@ public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetwork
/** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
- public static VcnWifiUnderlyingNetworkPriority fromPersistableBundle(
+ public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
final String ssid = in.getString(SSID_KEY);
- return new VcnWifiUnderlyingNetworkPriority(networkQuality, allowMetered, ssid);
+ return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid);
}
/** @hide */
@@ -74,11 +74,11 @@ public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetwork
return false;
}
- if (!(other instanceof VcnWifiUnderlyingNetworkPriority)) {
+ if (!(other instanceof VcnWifiUnderlyingNetworkTemplate)) {
return false;
}
- final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
+ final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
return mSsid.equals(rhs.mSsid);
}
@@ -94,8 +94,8 @@ public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetwork
return mSsid;
}
- /** This class is used to incrementally build VcnWifiUnderlyingNetworkPriority objects. */
- public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
+ public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
@Nullable private String mSsid;
/** Construct a Builder object. */
@@ -112,10 +112,10 @@ public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetwork
return this;
}
- /** Build the VcnWifiUnderlyingNetworkPriority. */
+ /** Build the VcnWifiUnderlyingNetworkTemplate. */
@NonNull
- public VcnWifiUnderlyingNetworkPriority build() {
- return new VcnWifiUnderlyingNetworkPriority(mNetworkQuality, mAllowMetered, mSsid);
+ public VcnWifiUnderlyingNetworkTemplate build() {
+ return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid);
}
/** @hide */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fa209cc02cd1..520730fa7352 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3374,6 +3374,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
/**
+ * Returns aggregated wake lock stats.
+ */
+ public abstract WakeLockStats getWakeLockStats();
+
+ /**
* Returns Timers tracking the total time of each Resource Power Manager state and voter.
*/
public abstract Map<String, ? extends Timer> getRpmStats();
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 7f526c13a75a..6339435e539c 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -157,6 +157,7 @@ public final class BatteryStatsManager {
@Retention(RetentionPolicy.SOURCE)
public @interface WifiSupplState {}
+
private final IBatteryStats mBatteryStats;
/** @hide */
@@ -352,6 +353,21 @@ public final class BatteryStatsManager {
}
/**
+ * Retrieves accumulate wake lock stats.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public WakeLockStats getWakeLockStats() {
+ try {
+ return mBatteryStats.getWakeLockStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates an app acquiring full wifi lock.
*
* @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/NewUserResponse.java b/core/java/android/os/NewUserResponse.java
index 3869559dc3c6..c2aef8e62078 100644
--- a/core/java/android/os/NewUserResponse.java
+++ b/core/java/android/os/NewUserResponse.java
@@ -17,6 +17,7 @@ package android.os;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
/**
* Contains the response of the call {@link UserManager#createUser(NewUserRequest)}.
@@ -29,7 +30,11 @@ public final class NewUserResponse {
private final @Nullable UserHandle mUser;
private final @UserManager.UserOperationResult int mOperationResult;
- NewUserResponse(@Nullable UserHandle user,
+ /**
+ * @hide
+ */
+ @TestApi
+ public NewUserResponse(@Nullable UserHandle user,
@UserManager.UserOperationResult int operationResult) {
mUser = user;
mOperationResult = operationResult;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 74fffd0ae10d..92d652df35d9 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1011,7 +1011,7 @@ public final class PowerManager {
new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
CACHE_KEY_IS_POWER_SAVE_MODE_PROPERTY) {
@Override
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
return mService.isPowerSaveMode();
} catch (RemoteException e) {
@@ -1024,7 +1024,7 @@ public final class PowerManager {
new PropertyInvalidatedCache<Void, Boolean>(MAX_CACHE_ENTRIES,
CACHE_KEY_IS_INTERACTIVE_PROPERTY) {
@Override
- protected Boolean recompute(Void query) {
+ public Boolean recompute(Void query) {
try {
return mService.isInteractive();
} catch (RemoteException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b3639e419a56..b4f3fa0ae80c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2754,7 +2754,7 @@ public class UserManager {
new PropertyInvalidatedCache<Integer, Boolean>(
32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
@Override
- protected Boolean recompute(Integer query) {
+ public Boolean recompute(Integer query) {
try {
return mService.isUserUnlocked(query);
} catch (RemoteException re) {
@@ -2762,7 +2762,7 @@ public class UserManager {
}
}
@Override
- protected boolean bypass(Integer query) {
+ public boolean bypass(Integer query) {
return query < 0;
}
};
@@ -2772,7 +2772,7 @@ public class UserManager {
new PropertyInvalidatedCache<Integer, Boolean>(
32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
@Override
- protected Boolean recompute(Integer query) {
+ public Boolean recompute(Integer query) {
try {
return mService.isUserUnlockingOrUnlocked(query);
} catch (RemoteException re) {
@@ -2780,7 +2780,7 @@ public class UserManager {
}
}
@Override
- protected boolean bypass(Integer query) {
+ public boolean bypass(Integer query) {
return query < 0;
}
};
diff --git a/core/java/android/os/WakeLockStats.aidl b/core/java/android/os/WakeLockStats.aidl
new file mode 100644
index 000000000000..be08d782264f
--- /dev/null
+++ b/core/java/android/os/WakeLockStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable WakeLockStats;
diff --git a/core/java/android/os/WakeLockStats.java b/core/java/android/os/WakeLockStats.java
new file mode 100644
index 000000000000..05a7313d1600
--- /dev/null
+++ b/core/java/android/os/WakeLockStats.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of wake lock stats.
+ * @hide
+ */
+public final class WakeLockStats implements Parcelable {
+
+ /** @hide */
+ public static class WakeLock {
+ public final int uid;
+ @NonNull
+ public final String name;
+ public final int timesAcquired;
+ public final long totalTimeHeldMs;
+
+ /**
+ * Time in milliseconds that the lock has been held or 0 if not currently holding the lock
+ */
+ public final long timeHeldMs;
+
+ public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
+ long timeHeldMs) {
+ this.uid = uid;
+ this.name = name;
+ this.timesAcquired = timesAcquired;
+ this.totalTimeHeldMs = totalTimeHeldMs;
+ this.timeHeldMs = timeHeldMs;
+ }
+
+ private WakeLock(Parcel in) {
+ uid = in.readInt();
+ name = in.readString();
+ timesAcquired = in.readInt();
+ totalTimeHeldMs = in.readLong();
+ timeHeldMs = in.readLong();
+ }
+
+ private void writeToParcel(Parcel out) {
+ out.writeInt(uid);
+ out.writeString(name);
+ out.writeInt(timesAcquired);
+ out.writeLong(totalTimeHeldMs);
+ out.writeLong(timeHeldMs);
+ }
+
+ @Override
+ public String toString() {
+ return "WakeLock{"
+ + "uid=" + uid
+ + ", name='" + name + '\''
+ + ", timesAcquired=" + timesAcquired
+ + ", totalTimeHeldMs=" + totalTimeHeldMs
+ + ", timeHeldMs=" + timeHeldMs
+ + '}';
+ }
+ }
+
+ private final List<WakeLock> mWakeLocks;
+
+ /** @hide **/
+ public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
+ mWakeLocks = wakeLocks;
+ }
+
+ @NonNull
+ public List<WakeLock> getWakeLocks() {
+ return mWakeLocks;
+ }
+
+ private WakeLockStats(Parcel in) {
+ final int size = in.readInt();
+ mWakeLocks = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mWakeLocks.add(new WakeLock(in));
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ final int size = mWakeLocks.size();
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ WakeLock stats = mWakeLocks.get(i);
+ stats.writeToParcel(out);
+ }
+ }
+
+ @NonNull
+ public static final Creator<WakeLockStats> CREATOR =
+ new Creator<WakeLockStats>() {
+ public WakeLockStats createFromParcel(Parcel in) {
+ return new WakeLockStats(in);
+ }
+
+ public WakeLockStats[] newArray(int size) {
+ return new WakeLockStats[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "WakeLockStats " + mWakeLocks;
+ }
+}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 19e04e69f52e..8ee52c21e869 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -99,7 +99,7 @@ public final class StorageVolume implements Parcelable {
@UnsupportedAppUsage
private final boolean mRemovable;
private final boolean mEmulated;
- private final boolean mStub;
+ private final boolean mExternallyManaged;
private final boolean mAllowMassStorage;
private final long mMaxFileSize;
private final UserHandle mOwner;
@@ -138,7 +138,7 @@ public final class StorageVolume implements Parcelable {
/** {@hide} */
public StorageVolume(String id, File path, File internalPath, String description,
- boolean primary, boolean removable, boolean emulated, boolean stub,
+ boolean primary, boolean removable, boolean emulated, boolean externallyManaged,
boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid,
String state) {
mId = Preconditions.checkNotNull(id);
@@ -148,7 +148,7 @@ public final class StorageVolume implements Parcelable {
mPrimary = primary;
mRemovable = removable;
mEmulated = emulated;
- mStub = stub;
+ mExternallyManaged = externallyManaged;
mAllowMassStorage = allowMassStorage;
mMaxFileSize = maxFileSize;
mOwner = Preconditions.checkNotNull(owner);
@@ -165,7 +165,7 @@ public final class StorageVolume implements Parcelable {
mPrimary = in.readInt() != 0;
mRemovable = in.readInt() != 0;
mEmulated = in.readInt() != 0;
- mStub = in.readInt() != 0;
+ mExternallyManaged = in.readInt() != 0;
mAllowMassStorage = in.readInt() != 0;
mMaxFileSize = in.readLong();
mOwner = in.readParcelable(null, android.os.UserHandle.class);
@@ -275,13 +275,13 @@ public final class StorageVolume implements Parcelable {
}
/**
- * Returns true if the volume is a stub volume (a volume managed from outside Android).
+ * Returns true if the volume is managed from outside Android.
*
* @hide
*/
@SystemApi
- public boolean isStub() {
- return mStub;
+ public boolean isExternallyManaged() {
+ return mExternallyManaged;
}
/**
@@ -520,7 +520,7 @@ public final class StorageVolume implements Parcelable {
pw.printPair("mPrimary", mPrimary);
pw.printPair("mRemovable", mRemovable);
pw.printPair("mEmulated", mEmulated);
- pw.printPair("mStub", mStub);
+ pw.printPair("mExternallyManaged", mExternallyManaged);
pw.printPair("mAllowMassStorage", mAllowMassStorage);
pw.printPair("mMaxFileSize", mMaxFileSize);
pw.printPair("mOwner", mOwner);
@@ -555,7 +555,7 @@ public final class StorageVolume implements Parcelable {
parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
- parcel.writeInt(mStub ? 1 : 0);
+ parcel.writeInt(mExternallyManaged ? 1 : 0);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
parcel.writeLong(mMaxFileSize);
parcel.writeParcelable(mOwner, flags);
@@ -637,7 +637,7 @@ public final class StorageVolume implements Parcelable {
mPrimary,
mRemovable,
mEmulated,
- /* stub= */ false,
+ /* externallyManaged= */ false,
/* allowMassStorage= */ false,
/* maxFileSize= */ 0,
mOwner,
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index ebd143c33990..84b6b5eabda1 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -404,7 +404,7 @@ public class VolumeInfo implements Parcelable {
final boolean removable;
final boolean emulated;
- final boolean stub = type == TYPE_STUB;
+ final boolean externallyManaged = type == TYPE_STUB;
final boolean allowMassStorage = false;
final String envState = reportUnmounted
? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
@@ -460,8 +460,8 @@ public class VolumeInfo implements Parcelable {
}
return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
- emulated, stub, allowMassStorage, maxFileSize, new UserHandle(userId), uuid,
- derivedFsUuid, envState);
+ emulated, externallyManaged, allowMassStorage, maxFileSize, new UserHandle(userId),
+ uuid, derivedFsUuid, envState);
}
@UnsupportedAppUsage
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index eac09aab46cd..1e5cdd79f539 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -105,7 +105,7 @@ public class VolumeRecord implements Parcelable {
final boolean primary = false;
final boolean removable = true;
final boolean emulated = false;
- final boolean stub = false;
+ final boolean externallyManaged = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0;
final UserHandle user = new UserHandle(UserHandle.USER_NULL);
@@ -117,8 +117,8 @@ public class VolumeRecord implements Parcelable {
}
return new StorageVolume(id, userPath, internalPath, description, primary, removable,
- emulated, stub, allowMassStorage, maxFileSize, user, null /* uuid */, fsUuid,
- envState);
+ emulated, externallyManaged, allowMassStorage, maxFileSize, user, null /* uuid */,
+ fsUuid, envState);
}
public void dump(IndentingPrintWriter pw) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 61e48c587e1b..3ea50e98879b 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1447,7 +1447,7 @@ public final class PermissionManager {
new PropertyInvalidatedCache<PermissionQuery, Integer>(
2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
@Override
- protected Integer recompute(PermissionQuery query) {
+ public Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid);
}
};
@@ -1530,12 +1530,12 @@ public final class PermissionManager {
new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
@Override
- protected Integer recompute(PackageNamePermissionQuery query) {
+ public Integer recompute(PackageNamePermissionQuery query) {
return checkPackageNamePermissionUncached(
query.permName, query.pkgName, query.userId);
}
@Override
- protected boolean bypass(PackageNamePermissionQuery query) {
+ public boolean bypass(PackageNamePermissionQuery query) {
return query.userId < 0;
}
};
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index dd2ea81d747b..5d00b29eb3c8 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -231,7 +231,7 @@ public class BlockedNumberContract {
prefix = { "STATUS_" },
value = {STATUS_NOT_BLOCKED, STATUS_BLOCKED_IN_LIST, STATUS_BLOCKED_RESTRICTED,
STATUS_BLOCKED_UNKNOWN_NUMBER, STATUS_BLOCKED_PAYPHONE,
- STATUS_BLOCKED_NOT_IN_CONTACTS})
+ STATUS_BLOCKED_NOT_IN_CONTACTS, STATUS_BLOCKED_UNAVAILABLE})
public @interface BlockStatus {}
/**
@@ -277,6 +277,13 @@ public class BlockedNumberContract {
public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
/**
+ * Integer reason code used with {@link #RES_BLOCK_STATUS} to indicate that a call was blocked
+ * because it is from a number not available.
+ * @hide
+ */
+ public static final int STATUS_BLOCKED_UNAVAILABLE = 6;
+
+ /**
* Integer reason indicating whether a call was blocked, and if so why.
* @hide
*/
@@ -441,6 +448,9 @@ public class BlockedNumberContract {
/* Preference key for whether should show an emergency call notification. */
public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
"show_emergency_call_notification";
+ /* Preference key of block unavailable calls setting. */
+ public static final String ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE =
+ "block_unavailable_calls_setting";
/**
* Notifies the provider that emergency services were contacted by the user.
@@ -547,6 +557,7 @@ public class BlockedNumberContract {
* {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+ * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
* {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
* @return {@code true} if the setting is enabled. {@code false} otherwise.
*/
@@ -574,6 +585,7 @@ public class BlockedNumberContract {
* {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
* {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+ * {@link #ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE}
* {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
* @param value the enabled statue of the setting to set.
*/
@@ -603,6 +615,8 @@ public class BlockedNumberContract {
return "blocked - payphone";
case STATUS_BLOCKED_NOT_IN_CONTACTS:
return "blocked - not in contacts";
+ case STATUS_BLOCKED_UNAVAILABLE:
+ return "blocked - unavailable";
}
return "unknown";
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0cc5bfda75e8..8dca7abaca8e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -910,6 +910,7 @@ public class CallLog {
* <li>{@link #PRESENTATION_RESTRICTED}</li>
* <li>{@link #PRESENTATION_UNKNOWN}</li>
* <li>{@link #PRESENTATION_PAYPHONE}</li>
+ * <li>{@link #PRESENTATION_UNAVAILABLE}</li>
* </ul>
* </p>
*
@@ -925,6 +926,8 @@ public class CallLog {
public static final int PRESENTATION_UNKNOWN = 3;
/** Number is a pay phone. */
public static final int PRESENTATION_PAYPHONE = 4;
+ /** Number is unavailable. */
+ public static final int PRESENTATION_UNAVAILABLE = 5;
/**
* The ISO 3166-1 two letters country code of the country where the
@@ -2028,6 +2031,10 @@ public class CallLog {
return presentation;
}
+ if (presentation == TelecomManager.PRESENTATION_UNAVAILABLE) {
+ return PRESENTATION_UNAVAILABLE;
+ }
+
if (TextUtils.isEmpty(number)
|| presentation == TelecomManager.PRESENTATION_UNKNOWN) {
return PRESENTATION_UNKNOWN;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 55ffdab9d094..44476795774e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10248,6 +10248,13 @@ public final class Settings {
public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
/**
+ * Whether the following typing focus feature for magnification is enabled.
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED =
+ "accessibility_magnification_follow_typing_enabled";
+
+ /**
* Controls magnification capability. Accessibility magnification is capable of at least one
* of the magnification modes.
*
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 6411314f7542..50efbac76f48 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -80,6 +80,7 @@ public abstract class ContentSuggestionsService extends Service {
colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
}
wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
+ contextImage.close();
}
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 3ee1a9000188..4ee02f01f330 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -16,6 +16,9 @@
package android.text;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -85,6 +88,37 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
}
/**
+ * Utility function to construct a BoringLayout instance.
+ *
+ * The spacing multiplier and additional amount spacing are not used by BoringLayout.
+ * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
+ * return 0.0.
+ *
+ * @param source the text to render
+ * @param paint the default paint for the layout
+ * @param outerWidth the wrapping width for the text
+ * @param align whether to left, right, or center the text
+ * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+ * line width
+ * @param includePad set whether to include extra space beyond font ascent and descent which is
+ * needed to avoid clipping in some scripts
+ * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+ * requested width
+ * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+ * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+ * not used, {@code outerWidth} is used instead
+ */
+ public static @NonNull BoringLayout make(
+ @NonNull CharSequence source, @NonNull TextPaint paint,
+ @IntRange(from = 0) int outerWidth,
+ @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics,
+ boolean includePad, @NonNull TextUtils.TruncateAt ellipsize,
+ @IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) {
+ return new BoringLayout(source, paint, outerWidth, align, 1f, 0f, metrics, includePad,
+ ellipsize, ellipsizedWidth, useFallbackLineSpacing);
+ }
+
+ /**
* Returns a BoringLayout for the specified text, potentially reusing
* this one if it is already suitable. The caller must make sure that
* no one is still using this Layout.
@@ -109,7 +143,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedStart = 0;
mEllipsizedCount = 0;
- init(source, paint, align, metrics, includePad, true);
+ init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
return this;
}
@@ -118,12 +152,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* this one if it is already suitable. The caller must make sure that
* no one is still using this Layout.
*
+ * The spacing multiplier and additional amount spacing are not used by BoringLayout.
+ * {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
+ * return 0.0.
+ *
* @param source the text to render
* @param paint the default paint for the layout
* @param outerWidth the wrapping width for the text
* @param align whether to left, right, or center the text
- * @param spacingMult this value is no longer used by BoringLayout
- * @param spacingAdd this value is no longer used by BoringLayout
* @param metrics {@code #Metrics} instance that contains information about FontMetrics and
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
@@ -134,13 +170,15 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerwidth} is used instead
*/
- public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth,
- Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
- boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ public @NonNull BoringLayout replaceOrMake(@NonNull CharSequence source,
+ @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth,
+ @NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad,
+ @NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
+ boolean useFallbackLineSpacing) {
boolean trust;
if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
- replaceWith(source, paint, outerWidth, align, spacingMult, spacingAdd);
+ replaceWith(source, paint, outerWidth, align, 1f, 0f);
mEllipsizedWidth = outerWidth;
mEllipsizedStart = 0;
@@ -148,17 +186,46 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
trust = true;
} else {
replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
- paint, outerWidth, align, spacingMult, spacingAdd);
+ paint, outerWidth, align, 1f, 0f);
mEllipsizedWidth = ellipsizedWidth;
trust = false;
}
- init(getText(), paint, align, metrics, includePad, trust);
+ init(getText(), paint, align, metrics, includePad, trust,
+ useFallbackLineSpacing);
return this;
}
/**
+ * Returns a BoringLayout for the specified text, potentially reusing
+ * this one if it is already suitable. The caller must make sure that
+ * no one is still using this Layout.
+ *
+ * @param source the text to render
+ * @param paint the default paint for the layout
+ * @param outerWidth the wrapping width for the text
+ * @param align whether to left, right, or center the text
+ * @param spacingMult this value is no longer used by BoringLayout
+ * @param spacingAdd this value is no longer used by BoringLayout
+ * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+ * line width
+ * @param includePad set whether to include extra space beyond font ascent and descent which is
+ * needed to avoid clipping in some scripts
+ * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+ * requested width
+ * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+ * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+ * not used, {@code outerwidth} is used instead
+ */
+ public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth,
+ Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
+ boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ return replaceOrMake(source, paint, outerWidth, align, metrics,
+ includePad, ellipsize, ellipsizedWidth, false /* useFallbackLineSpacing */);
+ }
+
+ /**
* @param source the text to render
* @param paint the default paint for the layout
* @param outerwidth the wrapping width for the text
@@ -178,7 +245,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedStart = 0;
mEllipsizedCount = 0;
- init(source, paint, align, metrics, includePad, true);
+ init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
}
/**
@@ -202,6 +269,34 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+ this(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics, includePad,
+ ellipsize, ellipsizedWidth, false /* fallbackLineSpacing */);
+ }
+
+ /**
+ *
+ * @param source the text to render
+ * @param paint the default paint for the layout
+ * @param outerWidth the wrapping width for the text
+ * @param align whether to left, right, or center the text
+ * @param spacingMult this value is no longer used by BoringLayout
+ * @param spacingAdd this value is no longer used by BoringLayout
+ * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
+ * line width
+ * @param includePad set whether to include extra space beyond font ascent and descent which is
+ * needed to avoid clipping in some scripts
+ * @param ellipsize whether to ellipsize the text if width of the text is longer than the
+ * requested {@code outerwidth}
+ * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
+ * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
+ * not used, {@code outerwidth} is used instead
+ */
+ public BoringLayout(
+ @NonNull CharSequence source, @NonNull TextPaint paint,
+ @IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult,
+ float spacingAdd, @NonNull BoringLayout.Metrics metrics, boolean includePad,
+ @NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
+ boolean useFallbackLineSpacing) {
/*
* It is silly to have to call super() and then replaceWith(),
* but we can't use "this" for the callback until the call to
@@ -224,11 +319,12 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
trust = false;
}
- init(getText(), paint, align, metrics, includePad, trust);
+ init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
}
/* package */ void init(CharSequence source, TextPaint paint, Alignment align,
- BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth) {
+ BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth,
+ boolean useFallbackLineSpacing) {
int spacing;
if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) {
@@ -260,7 +356,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
TextLine line = TextLine.obtain();
line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
- mEllipsizedStart, mEllipsizedStart + mEllipsizedCount);
+ mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
mMax = (int) Math.ceil(line.metrics(null));
TextLine.recycle(line);
}
@@ -336,6 +432,24 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
@UnsupportedAppUsage
public static Metrics isBoring(CharSequence text, TextPaint paint,
TextDirectionHeuristic textDir, Metrics metrics) {
+ return isBoring(text, paint, textDir, false /* useFallbackLineSpacing */, metrics);
+ }
+
+ /**
+ * Returns null if not boring; the width, ascent, and descent in the
+ * provided Metrics object (or a new one if the provided one was null)
+ * if boring.
+ *
+ * @param text a text to be calculated text layout.
+ * @param paint a paint object used for styling.
+ * @param textDir a text direction.
+ * @param useFallbackLineSpacing true if use fallback line spacing, otherwise false.
+ * @param metrics the out metrics.
+ * @return metrics on success. null if text cannot be rendered by BoringLayout.
+ */
+ public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
+ @Nullable Metrics metrics) {
final int textLength = text.length();
if (hasAnyInterestingChars(text, textLength)) {
return null; // There are some interesting characters. Not boring.
@@ -362,7 +476,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
- 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */);
+ 0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
+ useFallbackLineSpacing);
fm.width = (int) Math.ceil(line.metrics(fm));
TextLine.recycle(line);
@@ -450,6 +565,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
return mEllipsizedWidth;
}
+ @Override
+ public boolean isFallbackLineSpacingEnabled() {
+ return mUseFallbackLineSpacing;
+ }
+
// Override draw so it will be faster.
@Override
public void draw(Canvas c, Path highlight, Paint highlightpaint,
@@ -471,6 +591,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
private String mDirect;
private Paint mPaint;
+ private boolean mUseFallbackLineSpacing;
/* package */ int mBottom, mDesc; // for Direct
private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index da3e9b6d509c..95adb7765f1e 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -591,7 +591,8 @@ public abstract class Layout {
} else {
tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops,
getEllipsisStart(lineNum),
- getEllipsisStart(lineNum) + getEllipsisCount(lineNum));
+ getEllipsisStart(lineNum) + getEllipsisCount(lineNum),
+ isFallbackLineSpacingEnabled());
if (justify) {
tl.justify(right - left - indentWidth);
}
@@ -960,6 +961,15 @@ public abstract class Layout {
}
/**
+ * Return true if the fallback line space is enabled in this Layout.
+ *
+ * @return true if the fallback line space is enabled. Otherwise returns false.
+ */
+ public boolean isFallbackLineSpacingEnabled() {
+ return false;
+ }
+
+ /**
* Returns true if the character at offset and the preceding character
* are at different run levels (and thus there's a split caret).
* @param offset the offset
@@ -1231,7 +1241,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
float wid = tl.measure(offset - start, trailing, null);
TextLine.recycle(tl);
@@ -1271,7 +1282,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
boolean[] trailings = primaryIsTrailingPreviousAllLineOffsets(line);
if (!primary) {
for (int offset = 0; offset < trailings.length; ++offset) {
@@ -1456,7 +1468,8 @@ public abstract class Layout {
paint.setStartHyphenEdit(getStartHyphenEdit(line));
paint.setEndHyphenEdit(getEndHyphenEdit(line));
tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
@@ -1486,7 +1499,8 @@ public abstract class Layout {
paint.setStartHyphenEdit(getStartHyphenEdit(line));
paint.setEndHyphenEdit(getEndHyphenEdit(line));
tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
@@ -1572,7 +1586,8 @@ public abstract class Layout {
// XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here.
tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs,
false, null,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
final HorizontalMeasurementProvider horizontal =
new HorizontalMeasurementProvider(line, primary);
@@ -1828,7 +1843,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
// XXX: we don't care about tabs
tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null,
- getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
+ getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
+ isFallbackLineSpacingEnabled());
caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
TextLine.recycle(tl);
return caret;
@@ -2202,7 +2218,8 @@ public abstract class Layout {
}
}
tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
- 0 /* ellipsisStart */, 0 /* ellipsisEnd */);
+ 0 /* ellipsisStart */, 0 /* ellipsisEnd */,
+ false /* use fallback line spacing. unused */);
return margin + Math.abs(tl.metrics(null));
} finally {
TextLine.recycle(tl);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4789231b0404..b1bc7667da16 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -612,7 +612,6 @@ public class StaticLayout extends Layout {
TextPaint paint = b.mPaint;
int outerWidth = b.mWidth;
TextDirectionHeuristic textDir = b.mTextDir;
- final boolean fallbackLineSpacing = b.mFallbackLineSpacing;
float spacingmult = b.mSpacingMult;
float spacingadd = b.mSpacingAdd;
float ellipsizedWidth = b.mEllipsizedWidth;
@@ -630,6 +629,7 @@ public class StaticLayout extends Layout {
mLineCount = 0;
mEllipsized = false;
mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT;
+ mFallbackLineSpacing = b.mFallbackLineSpacing;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
@@ -867,17 +867,17 @@ public class StaticLayout extends Layout {
boolean moreChars = (endPos < bufEnd);
- final int ascent = fallbackLineSpacing
+ final int ascent = mFallbackLineSpacing
? Math.min(fmAscent, Math.round(ascents[breakIndex]))
: fmAscent;
- final int descent = fallbackLineSpacing
+ final int descent = mFallbackLineSpacing
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
// The fallback ascent/descent may be larger than top/bottom of the default font
// metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
// clipping.
- if (fallbackLineSpacing) {
+ if (mFallbackLineSpacing) {
if (ascent < fmTop) {
fmTop = ascent;
}
@@ -1381,6 +1381,11 @@ public class StaticLayout extends Layout {
return mEllipsizedWidth;
}
+ @Override
+ public boolean isFallbackLineSpacingEnabled() {
+ return mFallbackLineSpacing;
+ }
+
/**
* Return the total height of this layout.
*
@@ -1407,6 +1412,7 @@ public class StaticLayout extends Layout {
@UnsupportedAppUsage
private int mColumns;
private int mEllipsizedWidth;
+ private boolean mFallbackLineSpacing;
/**
* Keeps track if ellipsize is applied to the text.
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1a7ec7f99c95..2b396612cf3c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -71,6 +71,8 @@ public class TextLine {
private Spanned mSpanned;
private PrecomputedText mComputed;
+ private boolean mUseFallbackExtent = false;
+
// The start and end of a potentially existing ellipsis on this text line.
// We use them to filter out replacement and metric affecting spans on ellipsized away chars.
private int mEllipsisStart;
@@ -141,6 +143,7 @@ public class TextLine {
tl.mTabs = null;
tl.mChars = null;
tl.mComputed = null;
+ tl.mUseFallbackExtent = false;
tl.mMetricAffectingSpanSpanSet.recycle();
tl.mCharacterStyleSpanSet.recycle();
@@ -171,17 +174,20 @@ public class TextLine {
* @param ellipsisStart the start of the ellipsis relative to the line
* @param ellipsisEnd the end of the ellipsis relative to the line. When there
* is no ellipsis, this should be equal to ellipsisStart.
+ * @param useFallbackLineSpacing true for enabling fallback line spacing. false for disabling
+ * fallback line spacing.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
Directions directions, boolean hasTabs, TabStops tabStops,
- int ellipsisStart, int ellipsisEnd) {
+ int ellipsisStart, int ellipsisEnd, boolean useFallbackLineSpacing) {
mPaint = paint;
mText = text;
mStart = start;
mLen = limit - start;
mDir = dir;
mDirections = directions;
+ mUseFallbackExtent = useFallbackLineSpacing;
if (mDirections == null) {
throw new IllegalArgumentException("Directions cannot be null");
}
@@ -845,6 +851,31 @@ public class TextLine {
previousLeading);
}
+ private void expandMetricsFromPaint(TextPaint wp, int start, int end,
+ int contextStart, int contextEnd, boolean runIsRtl, FontMetricsInt fmi) {
+
+ final int previousTop = fmi.top;
+ final int previousAscent = fmi.ascent;
+ final int previousDescent = fmi.descent;
+ final int previousBottom = fmi.bottom;
+ final int previousLeading = fmi.leading;
+
+ if (mCharsValid) {
+ int count = end - start;
+ int contextCount = contextEnd - contextStart;
+ wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl,
+ fmi);
+ } else {
+ int delta = mStart;
+ wp.getFontMetricsInt(mText, delta + start, delta + end,
+ delta + contextStart, delta + contextEnd, runIsRtl, fmi);
+ }
+
+ updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
+ previousLeading);
+ }
+
+
static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
int previousDescent, int previousBottom, int previousLeading) {
fmi.top = Math.min(fmi.top, previousTop);
@@ -949,6 +980,10 @@ public class TextLine {
shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
}
+ if (mUseFallbackExtent && fmi != null) {
+ expandMetricsFromPaint(wp, start, end, contextStart, contextEnd, runIsRtl, fmi);
+ }
+
if (c != null) {
if (wp.bgColor != 0) {
int previousColor = wp.getColor();
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index 02fd7b4470f0..a1d6cc8e283a 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -222,7 +222,8 @@ public class TextShaper {
mp.getDirections(0, count),
false /* tabstop is not supported */,
null,
- -1, -1 // ellipsis is not supported.
+ -1, -1, // ellipsis is not supported.
+ false /* fallback line spacing is not used */
);
tl.shape(consumer);
} finally {
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index af76cb914b42..1ed12f74ba2c 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -66,7 +66,12 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
* @hide
*/
public void setBatchingEnabled(boolean batchingEnabled) {
+ if (mBatchingEnabled == batchingEnabled) {
+ return;
+ }
+
mBatchingEnabled = batchingEnabled;
+ mHandler.removeCallbacks(mConsumeBatchedInputEvents);
if (!batchingEnabled) {
unscheduleBatchedInput();
mHandler.post(mConsumeBatchedInputEvents);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab33feae7c56..b7f9be70f7ce 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -231,6 +231,7 @@ public final class SurfaceControl implements Parcelable {
float shadowRadius);
private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor,
@Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
+ private static native boolean nativeGetDisplayDecorationSupport(IBinder displayToken);
private static native void nativeSetFrameRate(long transactionObj, long nativeObject,
float frameRate, int compatibility, int changeFrameRateStrategy);
@@ -2651,6 +2652,20 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Returns whether a display supports DISPLAY_DECORATION.
+ *
+ * @param displayToken
+ * The token for the display.
+ *
+ * @return Whether the display supports DISPLAY_DECORATION.
+ *
+ * @hide
+ */
+ public static boolean getDisplayDecorationSupport(IBinder displayToken) {
+ return nativeGetDisplayDecorationSupport(displayToken);
+ }
+
+ /**
* Adds a callback to be informed about SF's jank classification for a specific surface.
* @hide
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d0a5835f8203..49ece5ff1e6a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -972,9 +972,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
if (mViewVisibility) {
- mTmpTransaction.show(mSurfaceControl);
+ geometryTransaction.show(mSurfaceControl);
} else {
- mTmpTransaction.hide(mSurfaceControl);
+ geometryTransaction.hide(mSurfaceControl);
}
if (mSurfacePackage != null) {
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index 1cb6825e426e..722546eb06e4 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -67,4 +67,13 @@ import android.graphics.Rect;
*/
void onAccessibilityActionPerformed(int displayId);
+ /**
+ * Called when the user is performing dragging gesture. It is started after the offset
+ * between the down location and the move event location exceed
+ * {@link ViewConfiguration#getScaledTouchSlop()}.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDrag(int displayId);
+
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 60ce65153890..6284bc2f3513 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1073,7 +1073,7 @@ public class Editor {
com.android.internal.R.dimen.textview_error_popup_default_width);
final StaticLayout l = StaticLayout.Builder.obtain(text, 0, text.length(), tv.getPaint(),
defaultWidthInPixels)
- .setUseLineSpacingFromFallbacks(tv.mUseFallbackLineSpacing)
+ .setUseLineSpacingFromFallbacks(tv.isFallbackLineSpacingForStaticLayout())
.build();
float max = 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 014340197393..7327214c5389 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -48,6 +48,9 @@ import android.annotation.XmlRes;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.assist.AssistStructure;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -453,6 +456,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
+ /**
+ * This change ID enables the fallback text line spacing (line height) for BoringLayout.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long BORINGLAYOUT_FALLBACK_LINESPACING = 210923482L; // buganizer id
+
+ /**
+ * This change ID enables the fallback text line spacing (line height) for StaticLayout.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.P)
+ public static final long STATICLAYOUT_FALLBACK_LINESPACING = 37756858; // buganizer id
+
// System wide time for last cut, copy or text changed action.
static long sLastCutCopyOrTextChangedTime;
@@ -766,8 +785,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mListenerChanged = false;
// True if internationalized input should be used for numbers and date and time.
private final boolean mUseInternationalizedInput;
- // True if fallback fonts that end up getting used should be allowed to affect line spacing.
- /* package */ boolean mUseFallbackLineSpacing;
+
+ // Fallback fonts that end up getting used should be allowed to affect line spacing.
+ private static final int FALLBACK_LINE_SPACING_NONE = 0;
+ private static final int FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY = 1;
+ private static final int FALLBACK_LINE_SPACING_ALL = 2;
+
+ private int mUseFallbackLineSpacing;
// True if the view text can be padded for compat reasons, when the view is translated.
private final boolean mUseTextPaddingForUiTranslation;
@@ -1479,7 +1503,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
- mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
+ if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
+ mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_ALL;
+ } else if (CompatChanges.isChangeEnabled(STATICLAYOUT_FALLBACK_LINESPACING)) {
+ mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
+ } else {
+ mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_NONE;
+ }
// TODO(b/179693024): Use a ChangeId instead.
mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R;
@@ -4541,8 +4571,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_fallbackLineSpacing
*/
public void setFallbackLineSpacing(boolean enabled) {
- if (mUseFallbackLineSpacing != enabled) {
- mUseFallbackLineSpacing = enabled;
+ int fallbackStrategy;
+ if (enabled) {
+ if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
+ fallbackStrategy = FALLBACK_LINE_SPACING_ALL;
+ } else {
+ fallbackStrategy = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
+ }
+ } else {
+ fallbackStrategy = FALLBACK_LINE_SPACING_NONE;
+ }
+ if (mUseFallbackLineSpacing != fallbackStrategy) {
+ mUseFallbackLineSpacing = fallbackStrategy;
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -4560,7 +4600,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@InspectableProperty
public boolean isFallbackLineSpacing() {
- return mUseFallbackLineSpacing;
+ return mUseFallbackLineSpacing != FALLBACK_LINE_SPACING_NONE;
+ }
+
+ private boolean isFallbackLineSpacingForBoringLayout() {
+ return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL;
+ }
+
+ // Package privte for accessing from Editor.java
+ /* package */ boolean isFallbackLineSpacingForStaticLayout() {
+ return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL
+ || mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
}
/**
@@ -9148,7 +9198,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (hintBoring == UNKNOWN_BORING) {
hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
- mHintBoring);
+ isFallbackLineSpacingForBoringLayout(), mHintBoring);
if (hintBoring != null) {
mHintBoring = hintBoring;
}
@@ -9190,7 +9240,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
- .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+ .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9250,7 +9300,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
- .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+ .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9259,7 +9309,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
result = builder.build();
} else {
if (boring == UNKNOWN_BORING) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(), mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -9303,7 +9354,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
- .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+ .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9430,7 +9481,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(), mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -9463,7 +9515,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (hintDes < 0) {
- hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
+ hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
+ isFallbackLineSpacingForBoringLayout(), mHintBoring);
if (hintBoring != null) {
mHintBoring = hintBoring;
}
@@ -9667,7 +9720,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
layoutBuilder.setAlignment(getLayoutAlignment())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
- .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
+ .setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(getBreakStrategy())
.setHyphenationFrequency(getHyphenationFrequency())
.setJustificationMode(getJustificationMode())
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1d13b73fc186..587876df0df6 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothActivityEnergyInfo;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ParcelFileDescriptor;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
@@ -157,6 +158,10 @@ interface IBatteryStats {
/** {@hide} */
GpsBatteryStats getGpsBatteryStats();
+ /** {@hide} */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+ WakeLockStats getWakeLockStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 0d4ad382222f..a33b2f18819e 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -41,6 +41,8 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
@@ -181,6 +183,8 @@ public class InteractionJankMonitor {
public static final int CUJ_SPLASHSCREEN_EXIT_ANIM = 39;
public static final int CUJ_SCREEN_OFF = 40;
public static final int CUJ_SCREEN_OFF_SHOW_AOD = 41;
+ public static final int CUJ_ONE_HANDED_ENTER_TRANSITION = 42;
+ public static final int CUJ_ONE_HANDED_EXIT_TRANSITION = 43;
private static final int NO_STATSD_LOGGING = -1;
@@ -231,6 +235,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLASHSCREEN_EXIT_ANIM,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_ENTER_TRANSITION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__ONE_HANDED_EXIT_TRANSITION,
};
private static volatile InteractionJankMonitor sInstance;
@@ -293,6 +299,8 @@ public class InteractionJankMonitor {
CUJ_SPLASHSCREEN_EXIT_ANIM,
CUJ_SCREEN_OFF,
CUJ_SCREEN_OFF_SHOW_AOD,
+ CUJ_ONE_HANDED_ENTER_TRANSITION,
+ CUJ_ONE_HANDED_EXIT_TRANSITION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -712,6 +720,10 @@ public class InteractionJankMonitor {
return "SCREEN_OFF";
case CUJ_SCREEN_OFF_SHOW_AOD:
return "SCREEN_OFF_SHOW_AOD";
+ case CUJ_ONE_HANDED_ENTER_TRANSITION:
+ return "ONE_HANDED_ENTER_TRANSITION";
+ case CUJ_ONE_HANDED_EXIT_TRANSITION:
+ return "ONE_HANDED_EXIT_TRANSITION";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 209c64a02324..21f719c74aa8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -60,6 +60,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
@@ -1143,6 +1144,37 @@ public class BatteryStatsImpl extends BatteryStats {
return mKernelWakelockStats;
}
+ @Override
+ public WakeLockStats getWakeLockStats() {
+ final long realtimeMs = mClock.elapsedRealtime();
+ final long realtimeUs = realtimeMs * 1000;
+ List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ final Uid uid = mUidStats.valueAt(i);
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+ uid.mWakelockStats.getMap();
+ for (int j = wakelockStats.size() - 1; j >= 0; j--) {
+ final String name = wakelockStats.keyAt(j);
+ final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
+ final DualTimer timer = wakelock.mTimerPartial;
+ if (timer != null) {
+ final long totalTimeLockHeldMs =
+ timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
+ if (totalTimeLockHeldMs != 0) {
+ uidWakeLockStats.add(
+ new WakeLockStats.WakeLock(uid.getUid(), name,
+ timer.getCountLocked(STATS_SINCE_CHARGED),
+ totalTimeLockHeldMs,
+ timer.isRunningLocked()
+ ? timer.getCurrentDurationMsLocked(realtimeMs)
+ : 0));
+ }
+ }
+ }
+ }
+ return new WakeLockStats(uidWakeLockStats);
+ }
+
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f3cdf827984f..a5cf7ce1d37c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -207,8 +207,10 @@ oneway interface IStatusBar
*
* @param displayId the ID of the display to notify.
* @param types the internal insets types of the bars are about to show transiently.
+ * @param isGestureOnSystemBar whether the gesture to show the transient bar was a gesture on
+ * one of the bars itself.
*/
- void showTransient(int displayId, in int[] types);
+ void showTransient(int displayId, in int[] types, boolean isGestureOnSystemBar);
/**
* Notifies System UI to abort the transient state of system bars, which prevents the bars being
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 04e4819f6ead..d3c3917cd791 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -178,7 +178,7 @@ public class ScreenshotHelper {
public ScreenshotHelper(Context context) {
mContext = context;
IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
}
/**
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
index 554e27890476..1c2d19d94871 100644
--- a/core/java/com/android/server/OWNERS
+++ b/core/java/com/android/server/OWNERS
@@ -1 +1 @@
-per-file SystemConfig.java = toddke@google.com,patb@google.com
+per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 67d0c52960e0..dd5af0435acc 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1768,6 +1768,15 @@ static void nativeSetGlobalShadowSettings(JNIEnv* env, jclass clazz, jfloatArray
client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
+static jboolean nativeGetDisplayDecorationSupport(JNIEnv* env, jclass clazz,
+ jobject displayTokenObject) {
+ sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject));
+ if (displayToken == nullptr) {
+ return JNI_FALSE;
+ }
+ return static_cast<jboolean>(SurfaceComposerClient::getDisplayDecorationSupport(displayToken));
+}
+
static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
@@ -2092,6 +2101,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeMirrorSurface },
{"nativeSetGlobalShadowSettings", "([F[FFFF)V",
(void*)nativeSetGlobalShadowSettings },
+ {"nativeGetDisplayDecorationSupport", "(Landroid/os/IBinder;)Z",
+ (void*)nativeGetDisplayDecorationSupport},
{"nativeGetHandle", "(J)J",
(void*)nativeGetHandle },
{"nativeSetFixedTransformHint", "(JJI)V",
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 00b7fd7f0887..4c0ba8cbe304 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -86,6 +86,8 @@ message SecureSettingsProto {
optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_fade_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // Setting for accessibility magnification for following typing.
+ optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7d44fd99fb2e..9449f606b3aa 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3263,6 +3263,7 @@
</staging-public-group>
<staging-public-group type="style" first-id="0x01dd0000">
+ <public name="TextAppearance.DeviceDefault.Headline" />
</staging-public-group>
<staging-public-group type="string" first-id="0x01dc0000">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index d8111ea5d8ba..dcfca8497ec6 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -398,7 +398,7 @@ easier.
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Headline" parent="TextAppearance.Material.Headline">
- <item name="fontFamily">@string/config_bodyFontFamily</item>
+ <item name="fontFamily">@string/config_headlineFontFamily</item>
</style>
<style name="TextAppearance.DeviceDefault.Display1" parent="TextAppearance.Material.Display1">
<item name="fontFamily">@string/config_bodyFontFamily</item>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 7a2c63f70dd1..fd3079fd295d 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -77,11 +77,11 @@ public class PropertyInvalidatedCacheTests {
PropertyInvalidatedCache<Integer, Boolean> testCache =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
@Override
- protected boolean bypass(Integer x) {
+ public boolean bypass(Integer x) {
return x % 13 == 0;
}
};
@@ -131,21 +131,21 @@ public class PropertyInvalidatedCacheTests {
PropertyInvalidatedCache<Integer, Boolean> cache1 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
PropertyInvalidatedCache<Integer, Boolean> cache2 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
PropertyInvalidatedCache<Integer, Boolean> cache3 =
new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
@@ -171,7 +171,7 @@ public class PropertyInvalidatedCacheTests {
// Create a new cache1. Verify that the new instance is disabled.
cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
@Override
- protected Boolean recompute(Integer x) {
+ public Boolean recompute(Integer x) {
return tester.query(x);
}
};
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 90ce305b3dab..412d6ec975ac 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -48,7 +48,7 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */,
- 0, 0 /* no ellipsis */);
+ 0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
final float originalWidth = tl.metrics(null);
final float expandedWidth = 2 * originalWidth;
@@ -105,7 +105,7 @@ public class TextLineTest {
tl.set(paint, str, 0, str.length(),
TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(str, 0, str.length()) ? -1 : 1,
layout.getLineDirections(0), tabStops != null, tabStops,
- 0, 0 /* no ellipsis */);
+ 0, 0 /* no ellipsis */, false /* useFallbackLineSpacing */);
return tl;
}
@@ -276,7 +276,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
- false /* hasTabs */, null /* tabStops */, 9, 12);
+ false /* hasTabs */, null /* tabStops */, 9, 12,
+ false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertFalse(span.mIsUsed);
@@ -292,7 +293,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
- false /* hasTabs */, null /* tabStops */, 9, 12);
+ false /* hasTabs */, null /* tabStops */, 9, 12,
+ false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertTrue(span.mIsUsed);
@@ -308,7 +310,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
- false /* hasTabs */, null /* tabStops */, 9, 12);
+ false /* hasTabs */, null /* tabStops */, 9, 12,
+ false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertTrue(span.mIsUsed);
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 62d0b2e0b52f..02e5942a3544 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -120,10 +120,18 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return null;
}
+ public Region getCurrentMagnificationRegion(int displayId) {
+ return null;
+ }
+
public boolean resetMagnification(int displayId, boolean animate) {
return false;
}
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
+ return false;
+ }
+
public boolean setMagnificationConfig(int displayId,
@NonNull MagnificationConfig config, boolean animate) {
return false;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index c1a45c47539d..388cf6e15e0b 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -22,6 +22,8 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_CACHED;
import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.os.BatteryStats;
+import android.os.WakeLockStats;
import android.util.SparseArray;
import android.view.Display;
@@ -50,6 +53,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
@@ -507,4 +512,51 @@ public class BatteryStatsImplTest {
final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(parentUid);
u.addIsolatedUid(childUid);
}
+
+ @Test
+ public void testGetWakeLockStats() {
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ // First wakelock, acquired once, not currently held
+ mMockClock.realtime = 1000;
+ mBatteryStatsImpl.noteStartWakeLocked(10100, 100, null, "wakeLock1", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 3000;
+ mBatteryStatsImpl.noteStopWakeLocked(10100, 100, null, "wakeLock1", null,
+ BatteryStats.WAKE_TYPE_PARTIAL);
+
+ // Second wakelock, acquired twice, still held
+ mMockClock.realtime = 4000;
+ mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 5000;
+ mBatteryStatsImpl.noteStopWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL);
+
+ mMockClock.realtime = 6000;
+ mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
+ BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 9000;
+
+ List<WakeLockStats.WakeLock> wakeLockStats =
+ mBatteryStatsImpl.getWakeLockStats().getWakeLocks();
+ assertThat(wakeLockStats).hasSize(2);
+
+ WakeLockStats.WakeLock wakeLock1 = wakeLockStats.stream()
+ .filter(wl -> wl.uid == 10100 && wl.name.equals("wakeLock1")).findFirst().get();
+
+ assertThat(wakeLock1.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock1.timeHeldMs).isEqualTo(0); // Not currently held
+ assertThat(wakeLock1.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
+
+ WakeLockStats.WakeLock wakeLock2 = wakeLockStats.stream()
+ .filter(wl -> wl.uid == 10200 && wl.name.equals("wakeLock2")).findFirst().get();
+
+ assertThat(wakeLock2.timesAcquired).isEqualTo(2);
+ assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
+ assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
+ }
}
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
index c4080e822a3f..182bf6d165a0 100644
--- a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
+++ b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
@@ -35,7 +35,7 @@ public class PropertyInvalidatedCacheTest extends TestCase {
}
@Override
- protected String recompute(Integer qv) {
+ public String recompute(Integer qv) {
mRecomputeCount += 1;
return "foo" + qv.toString();
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42e470b7f660..eefad8d0e4de 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Objects;
/**
* The Paint class holds the style and color information about how to draw
@@ -2131,6 +2132,116 @@ public class Paint {
}
/**
+ * Returns the font metrics value for the given text.
+ *
+ * If the text is rendered with multiple font files, this function returns the large ascent and
+ * descent that are enough for drawing all font files.
+ *
+ * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
+ * changes letter shape based on its location or surrounding characters.
+ *
+ * @param text a text to be measured.
+ * @param start a starting offset in the text.
+ * @param count a length of the text to be measured.
+ * @param contextStart a context starting offset in the text.
+ * @param contextCount a length of the context to be used.
+ * @param isRtl true if measuring on RTL context, otherwise false.
+ * @param outMetrics the output font metrics.
+ */
+ public void getFontMetricsInt(
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int count,
+ @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
+ boolean isRtl,
+ @NonNull FontMetricsInt outMetrics) {
+
+ if (text == null) {
+ throw new IllegalArgumentException("text must not be null");
+ }
+ if (start < 0 || start >= text.length()) {
+ throw new IllegalArgumentException("start argument is out of bounds.");
+ }
+ if (count < 0 || start + count > text.length()) {
+ throw new IllegalArgumentException("count argument is out of bounds.");
+ }
+ if (contextStart < 0 || contextStart >= text.length()) {
+ throw new IllegalArgumentException("ctxStart argument is out of bounds.");
+ }
+ if (contextCount < 0 || contextStart + contextCount > text.length()) {
+ throw new IllegalArgumentException("ctxCount argument is out of bounds.");
+ }
+ if (outMetrics == null) {
+ throw new IllegalArgumentException("outMetrics must not be null.");
+ }
+
+ if (count == 0) {
+ getFontMetricsInt(outMetrics);
+ return;
+ }
+
+ if (text instanceof String) {
+ nGetFontMetricsIntForText(mNativePaint, (String) text, start, count, contextStart,
+ contextCount, isRtl, outMetrics);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(contextCount);
+ TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
+ nGetFontMetricsIntForText(mNativePaint, buf, start - contextStart, count, 0,
+ contextCount, isRtl, outMetrics);
+ }
+
+ }
+
+ /**
+ * Returns the font metrics value for the given text.
+ *
+ * If the text is rendered with multiple font files, this function returns the large ascent and
+ * descent that are enough for drawing all font files.
+ *
+ * The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
+ * changes letter shape based on its location or surrounding characters.
+ *
+ * @param text a text to be measured.
+ * @param start a starting offset in the text.
+ * @param count a length of the text to be measured.
+ * @param contextStart a context starting offset in the text.
+ * @param contextCount a length of the context to be used.
+ * @param isRtl true if measuring on RTL context, otherwise false.
+ * @param outMetrics the output font metrics.
+ */
+ public void getFontMetricsInt(@NonNull char[] text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int count,
+ @IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
+ boolean isRtl,
+ @NonNull FontMetricsInt outMetrics) {
+ if (text == null) {
+ throw new IllegalArgumentException("text must not be null");
+ }
+ if (start < 0 || start >= text.length) {
+ throw new IllegalArgumentException("start argument is out of bounds.");
+ }
+ if (count < 0 || start + count > text.length) {
+ throw new IllegalArgumentException("count argument is out of bounds.");
+ }
+ if (contextStart < 0 || contextStart >= text.length) {
+ throw new IllegalArgumentException("ctxStart argument is out of bounds.");
+ }
+ if (contextCount < 0 || contextStart + contextCount > text.length) {
+ throw new IllegalArgumentException("ctxCount argument is out of bounds.");
+ }
+ if (outMetrics == null) {
+ throw new IllegalArgumentException("outMetrics must not be null.");
+ }
+
+ if (count == 0) {
+ getFontMetricsInt(outMetrics);
+ return;
+ }
+
+ nGetFontMetricsIntForText(mNativePaint, text, start, count, contextStart, contextCount,
+ isRtl, outMetrics);
+ }
+
+ /**
* Convenience method for callers that want to have FontMetrics values as
* integers.
*/
@@ -2163,6 +2274,23 @@ public class Paint {
" descent=" + descent + " bottom=" + bottom +
" leading=" + leading;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof FontMetricsInt)) return false;
+ FontMetricsInt that = (FontMetricsInt) o;
+ return top == that.top
+ && ascent == that.ascent
+ && descent == that.descent
+ && bottom == that.bottom
+ && leading == that.leading;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(top, ascent, descent, bottom, leading);
+ }
}
/**
@@ -3117,6 +3245,13 @@ public class Paint {
int contextStart, int contextEnd, boolean isRtl, int offset);
private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float advance);
+ private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,
+ int start, int count, int ctxStart, int ctxCount, boolean isRtl,
+ FontMetricsInt outMetrics);
+ private static native void nGetFontMetricsIntForText(long paintPtr, String text,
+ int start, int count, int ctxStart, int ctxCount, boolean isRtl,
+ FontMetricsInt outMetrics);
+
// ---------------- @FastNative ------------------------
@@ -3130,7 +3265,6 @@ public class Paint {
@FastNative
private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
-
// ---------------- @CriticalNative ------------------------
@CriticalNative
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a6aa4f21e53b..54bab4ad3780 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -276,7 +276,7 @@ import javax.security.auth.x500.X500Principal;
* "HMACSHA256", sharedSecret, salt, info.toByteArray(), 32));
* byte[] associatedData = {};
* return key.decrypt(ciphertext, associatedData);
- * }
+ * }</pre>
*/
public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
private static final X500Principal DEFAULT_ATTESTATION_CERT_SUBJECT =
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index 4ac972c6cfa7..44b2f45052ba 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -21,7 +21,7 @@
android:orientation="vertical"
android:clipToPadding="false"
android:paddingEnd="@dimen/compat_hint_padding_end"
- android:paddingBottom="5dp"
+ android:paddingBottom="8dp"
android:clickable="true">
<TextView
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index c99f3fe89563..dfaeeeb81c07 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -33,8 +33,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="16dp"
+ android:layout_marginEnd="@dimen/compat_button_margin"
+ android:layout_marginBottom="@dimen/compat_button_margin"
android:orientation="vertical">
<ImageButton
@@ -62,8 +62,10 @@
<ImageButton
android:id="@+id/size_compat_restart_button"
android:visibility="gone"
- android:layout_width="@dimen/size_compat_button_width"
- android:layout_height="@dimen/size_compat_button_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/compat_button_margin"
+ android:layout_marginBottom="@dimen/compat_button_margin"
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/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d338e3bd74f9..1c19a10ab231 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -200,11 +200,8 @@
<!-- Size of user education views on large screens (phone is just match parent). -->
<dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
- <!-- The width of the size compat restart button including padding. -->
- <dimen name="size_compat_button_width">80dp</dimen>
-
- <!-- The height of the size compat restart button including padding. -->
- <dimen name="size_compat_button_height">64dp</dimen>
+ <!-- Bottom and end margin for compat buttons. -->
+ <dimen name="compat_button_margin">16dp</dimen>
<!-- The radius of the corners of the compat hint bubble. -->
<dimen name="compat_hint_corner_radius">28dp</dimen>
@@ -212,8 +209,8 @@
<!-- The width of the compat hint point. -->
<dimen name="compat_hint_point_width">10dp</dimen>
- <!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
- - compat_hint_corner_radius - compat_hint_point_width /2). -->
+ <!-- The end padding for the compat hint. Computed as (compat button width (=48) / 2
+ + compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). -->
<dimen name="compat_hint_padding_end">7dp</dimen>
<!-- The width of the size compat hint. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 8d43f1375a8c..eaeade82ebb6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -108,6 +108,8 @@ public class Bubble implements BubbleViewProvider {
private Bitmap mBubbleBitmap;
// The app badge for the bubble
private Bitmap mBadgeBitmap;
+ // App badge without any markings for important conversations
+ private Bitmap mRawBadgeBitmap;
private int mDotColor;
private Path mDotPath;
private int mFlags;
@@ -248,6 +250,11 @@ public class Bubble implements BubbleViewProvider {
}
@Override
+ public Bitmap getRawAppBadge() {
+ return mRawBadgeBitmap;
+ }
+
+ @Override
public int getDotColor() {
return mDotColor;
}
@@ -409,6 +416,7 @@ public class Bubble implements BubbleViewProvider {
mFlyoutMessage = info.flyoutMessage;
mBadgeBitmap = info.badgeBitmap;
+ mRawBadgeBitmap = info.mRawBadgeBitmap;
mBubbleBitmap = info.bubbleBitmap;
mDotColor = info.dotColor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index ce1f8707da31..7903a5102dde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -284,17 +284,21 @@ public class BubbleController {
mSyncQueue = syncQueue;
}
- private static void registerOneHandedState(OneHandedController oneHanded) {
+ private void registerOneHandedState(OneHandedController oneHanded) {
oneHanded.registerTransitionCallback(
new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
- // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
}
@Override
public void onStopFinished(Rect bounds) {
- // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
}
});
}
@@ -423,7 +427,7 @@ public class BubbleController {
}
});
- mOneHandedOptional.ifPresent(BubbleController::registerOneHandedState);
+ mOneHandedOptional.ifPresent(this::registerOneHandedState);
}
@VisibleForTesting
@@ -1315,6 +1319,7 @@ public class BubbleController {
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
+ * Updates bubbles' icon views clickable states
*/
public void updateStack() {
if (mStackView == null) {
@@ -1332,6 +1337,8 @@ public class BubbleController {
}
mStackView.updateContentDescription();
+
+ mStackView.updateBubblesClickableStates();
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index a175929cf498..dd751d24e770 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -141,6 +141,10 @@ class BubbleOverflow(
return null
}
+ override fun getRawAppBadge(): Bitmap? {
+ return null
+ }
+
override fun getBubbleIcon(): Bitmap {
return bitmap
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 79b765356e9e..7bf4439410f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1485,6 +1485,25 @@ public class BubbleStackView extends FrameLayout
}
}
+ /**
+ * Update bubbles' icon views clickable states.
+ */
+ public void updateBubblesClickableStates() {
+ for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
+ final Bubble bubble = mBubbleData.getBubbles().get(i);
+ if (bubble.getIconView() != null) {
+ if (mIsExpanded) {
+ // when stack is expanded all bubbles are clickable
+ bubble.getIconView().setClickable(true);
+ } else {
+ // when stack is collapsed, only the top bubble needs to be clickable,
+ // so that a11y ignores all the inaccessible bubbles in the stack
+ bubble.getIconView().setClickable(i == 0);
+ }
+ }
+ }
+ }
+
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
@@ -2615,11 +2634,13 @@ public class BubbleStackView extends FrameLayout
// If available, update the manage menu's settings option with the expanded bubble's app
// name and icon.
- if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
+ if (show) {
final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
- mManageSettingsIcon.setImageBitmap(bubble.getAppBadge());
- mManageSettingsText.setText(getResources().getString(
- R.string.bubbles_app_settings, bubble.getAppName()));
+ if (bubble != null) {
+ mManageSettingsIcon.setImageBitmap(bubble.getRawAppBadge());
+ mManageSettingsText.setText(getResources().getString(
+ R.string.bubbles_app_settings, bubble.getAppName()));
+ }
}
if (mExpandedBubble.getExpandedView().getTaskView() != null) {
@@ -3013,6 +3034,16 @@ public class BubbleStackView extends FrameLayout
}
/**
+ * Handles vertical offset changes, e.g. when one handed mode is switched on/off.
+ *
+ * @param offset new vertical offset.
+ */
+ void onVerticalOffsetChanged(int offset) {
+ // adjust dismiss view vertical position, so that it is still visible to the user
+ mDismissView.setPadding(/* left = */ 0, /* top = */ 0, /* right = */ 0, offset);
+ }
+
+ /**
* Holds some commonly queried information about the stack.
*/
public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 91aff3e8a9e0..b01c756c5a7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -127,6 +127,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
String appName;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
+ Bitmap mRawBadgeBitmap;
int dotColor;
Path dotPath;
Bubble.FlyoutMessage flyoutMessage;
@@ -189,6 +190,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
+ // Raw badge bitmap never includes the important conversation ring
+ info.mRawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon;
info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
// Dot color & placement
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 7e552826e94a..3f6d41bb2b68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -43,6 +43,9 @@ public interface BubbleViewProvider {
/** App badge drawable to draw above bubble icon. */
@Nullable Bitmap getAppBadge();
+ /** Base app badge drawable without any markings. */
+ @Nullable Bitmap getRawAppBadge();
+
/** Path of normalized bubble icon to draw dot on. */
Path getDotPath();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 4f01dc60452e..cc37caec5225 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -22,6 +22,7 @@ import android.content.pm.LauncherApps;
import android.os.Handler;
import android.view.WindowManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
@@ -143,10 +144,10 @@ public class WMShellModule {
static OneHandedController provideOneHandedController(Context context,
WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
+ @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
return OneHandedController.create(context, windowManager, displayController, displayLayout,
- taskStackListener, uiEventLogger, mainExecutor, mainHandler);
+ taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c9c73fd8f191..96f82fa5ce36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -45,6 +45,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
@@ -194,7 +195,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
public static OneHandedController create(
Context context, WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
+ InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
+ ShellExecutor mainExecutor, Handler mainHandler) {
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
@@ -210,13 +212,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayLayout, settingsUtil, animationController, tutorialHandler,
- oneHandedBackgroundPanelOrganizer, mainExecutor);
+ oneHandedBackgroundPanelOrganizer, jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
+ settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, jankMonitor,
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
mainHandler);
}
@@ -232,6 +234,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
OneHandedState state,
+ InteractionJankMonitor jankMonitor,
OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index ec3ef5a27fe0..87eb40cbde62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.onehanded;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.window.DisplayAreaAppearedInfo;
@@ -34,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -41,6 +45,7 @@ import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Manages OneHanded display areas such as offset.
@@ -62,6 +67,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private final InteractionJankMonitor mJankMonitor;
+ private final Context mContext;
private boolean mIsReady;
private float mLastVisualOffset = 0;
@@ -95,7 +102,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ endCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -105,7 +116,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ cancelCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -121,11 +136,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
+ InteractionJankMonitor jankMonitor,
ShellExecutor mainExecutor) {
super(mainExecutor);
+ mContext = context;
setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
mAnimationController = animationController;
+ mJankMonitor = jankMonitor;
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
mEnterExitAnimationDurationMs =
@@ -197,6 +215,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ beginCUJTracing(CUJ_ONE_HANDED_ENTER_TRANSITION, "enterOneHanded");
+ } else {
+ beginCUJTracing(CUJ_ONE_HANDED_EXIT_TRANSITION, "stopOneHanded");
+ }
mDisplayAreaTokenMap.forEach(
(token, leash) -> {
animateWindows(token, leash, fromPos, yOffset, direction,
@@ -302,6 +325,26 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mTransitionCallbacks.add(callback);
}
+ void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) {
+ final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry =
+ getDisplayAreaTokenMap().entrySet().iterator().next();
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ cujType, mContext, firstEntry.getValue());
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ mJankMonitor.begin(builder);
+ }
+
+ void endCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.end(cujType);
+ }
+
+ void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.cancel(cujType);
+ }
+
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index c6794b84c42d..1716380c1f7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
-import static android.window.TransitionInfo.isIndependent;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -48,7 +47,6 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.util.ArrayMap;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -62,8 +60,8 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.util.CounterRotator;
import java.util.Optional;
@@ -175,14 +173,25 @@ public class PipTransition extends PipTransitionController {
return true;
}
+ // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
+ // happen when a new activity requests enter PIP). In this case, we just show this Task in
+ // its end state, and play other animation as normal.
+ final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+ if (currentPipChange != null
+ && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
+ resetPrevPip(currentPipChange, startTransaction);
+ }
+
// Entering PIP.
if (isEnteringPip(info, mCurrentPipTaskToken)) {
return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
}
- // For transition that we don't animate, we may need to update the PIP surface, otherwise it
- // will be reset after the transition.
- updatePipForUnhandledTransition(info, startTransaction, finishTransaction);
+ // For transition that we don't animate, but contains the PIP leash, we need to update the
+ // PIP surface, otherwise it will be reset after the transition.
+ if (currentPipChange != null) {
+ updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
+ }
return false;
}
@@ -322,35 +331,11 @@ public class PipTransition extends PipTransitionController {
final int displayH = displayRotationChange.getEndAbsBounds().height();
// Counter-rotate all "going-away" things since they are still in the old orientation.
- final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (!Transitions.isClosingType(change.getMode())
- || !isIndependent(change, info)
- || change.getParent() == null) {
- continue;
- }
- CounterRotator crot = counterRotators.get(change.getParent());
- if (crot == null) {
- crot = new CounterRotator();
- crot.setup(startTransaction,
- info.getChange(change.getParent()).getLeash(),
- rotateDelta, displayW, displayH);
- if (crot.getSurface() != null) {
- // Wallpaper should be placed at the bottom.
- final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
- ? info.getChanges().size() - i
- : -1;
- startTransaction.setLayer(crot.getSurface(), layer);
- }
- counterRotators.put(change.getParent(), crot);
- }
- crot.addChild(startTransaction, change.getLeash());
- }
+ final CounterRotatorHelper rotator = new CounterRotatorHelper();
+ rotator.handleClosingChanges(info, startTransaction, rotateDelta, displayW, displayH);
+
mFinishCallback = (wct, wctCB) -> {
- for (int i = 0; i < counterRotators.size(); ++i) {
- counterRotators.valueAt(i).cleanUp(info.getRootLeash());
- }
+ rotator.cleanUp();
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(wct, wctCB);
};
@@ -597,30 +582,36 @@ public class PipTransition extends PipTransitionController {
finishCallback.onTransitionFinished(null, null);
}
- private void updatePipForUnhandledTransition(@NonNull TransitionInfo info,
+ private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange,
+ @NonNull SurfaceControl.Transaction startTransaction) {
+ final SurfaceControl leash = prevPipChange.getLeash();
+ final Rect bounds = prevPipChange.getEndAbsBounds();
+ final Point offset = prevPipChange.getEndRelOffset();
+ bounds.offset(-offset.x, -offset.y);
+
+ startTransaction.setWindowCrop(leash, null);
+ startTransaction.setMatrix(leash, 1, 0, 0, 1);
+ startTransaction.setCornerRadius(leash, 0);
+ startTransaction.setPosition(leash, bounds.left, bounds.top);
+
+ mCurrentPipTaskToken = null;
+ mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
+ }
+
+ private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
- if (mCurrentPipTaskToken == null) {
- return;
- }
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (!mCurrentPipTaskToken.equals(change.getContainer())) {
- continue;
- }
- // When the PIP window is visible and being a part of the transition, such as display
- // rotation, we need to update its bounds and rounded corner.
- final SurfaceControl leash = change.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
- final boolean isInPip = mPipTransitionState.isInPip();
- mSurfaceTransactionHelper
- .crop(startTransaction, leash, destBounds)
- .round(startTransaction, leash, isInPip);
- mSurfaceTransactionHelper
- .crop(finishTransaction, leash, destBounds)
- .round(finishTransaction, leash, isInPip);
- break;
- }
+ // When the PIP window is visible and being a part of the transition, such as display
+ // rotation, we need to update its bounds and rounded corner.
+ final SurfaceControl leash = pipChange.getLeash();
+ final Rect destBounds = mPipBoundsState.getBounds();
+ final boolean isInPip = mPipTransitionState.isInPip();
+ mSurfaceTransactionHelper
+ .crop(startTransaction, leash, destBounds)
+ .round(startTransaction, leash, isInPip);
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destBounds)
+ .round(finishTransaction, leash, isInPip);
}
private void finishResizeForMenu(Rect destinationBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
new file mode 100644
index 000000000000..08c99b2c4a83
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.util.CounterRotator;
+
+import java.util.List;
+
+/**
+ * The helper class that performs counter-rotate for all "going-away" window containers if they are
+ * still in the old rotation in a transition.
+ */
+public class CounterRotatorHelper {
+ private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
+ private SurfaceControl mRootLeash;
+
+ /** Puts the surface controls of closing changes to counter-rotated surfaces. */
+ public void handleClosingChanges(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ int rotateDelta, int displayW, int displayH) {
+ mRootLeash = info.getRootLeash();
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ final int numChanges = changes.size();
+ for (int i = numChanges - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = changes.get(i);
+ final WindowContainerToken parent = change.getParent();
+ if (!Transitions.isClosingType(change.getMode())
+ || !TransitionInfo.isIndependent(change, info) || parent == null) {
+ continue;
+ }
+
+ CounterRotator crot = mRotatorMap.get(parent);
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction, info.getChange(parent).getLeash(), rotateDelta,
+ displayW, displayH);
+ final SurfaceControl rotatorSc = crot.getSurface();
+ if (rotatorSc != null) {
+ // Wallpaper should be placed at the bottom.
+ final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
+ ? numChanges - i
+ : -1;
+ startTransaction.setLayer(rotatorSc, layer);
+ }
+ mRotatorMap.put(parent, crot);
+ }
+ crot.addChild(startTransaction, change.getLeash());
+ }
+ }
+
+ /** Restores to the original state, i.e. reparent back to transition root. */
+ public void cleanUp() {
+ for (int i = mRotatorMap.size() - 1; i >= 0; --i) {
+ mRotatorMap.valueAt(i).cleanUp(mRootLeash);
+ }
+ mRotatorMap.clear();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 072b9252254e..5833ca80d384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -43,7 +43,6 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
-import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -78,7 +77,6 @@ import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -92,7 +90,6 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
@@ -280,16 +277,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+ final CounterRotatorHelper rotator = new CounterRotatorHelper();
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
- for (int i = 0; i < counterRotators.size(); ++i) {
- counterRotators.valueAt(i).cleanUp(info.getRootLeash());
- }
- counterRotators.clear();
-
+ rotator.cleanUp();
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -322,29 +315,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
continue;
}
} else {
- // opening/closing an app into a new orientation. Counter-rotate all
- // "going-away" things since they are still in the old orientation.
- for (int j = info.getChanges().size() - 1; j >= 0; --j) {
- final TransitionInfo.Change innerChange = info.getChanges().get(j);
- if (!Transitions.isClosingType(innerChange.getMode())
- || !isIndependent(innerChange, info)
- || innerChange.getParent() == null) {
- continue;
- }
- CounterRotator crot = counterRotators.get(innerChange.getParent());
- if (crot == null) {
- crot = new CounterRotator();
- crot.setup(startTransaction,
- info.getChange(innerChange.getParent()).getLeash(),
- rotateDelta, displayW, displayH);
- if (crot.getSurface() != null) {
- int layer = info.getChanges().size() - j;
- startTransaction.setLayer(crot.getSurface(), layer);
- }
- counterRotators.put(innerChange.getParent(), crot);
- }
- crot.addChild(startTransaction, innerChange.getLeash());
- }
+ // Opening/closing an app into a new orientation.
+ rotator.handleClosingChanges(info, startTransaction, rotateDelta,
+ displayW, displayH);
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index dbd3d8cde42a..8dd91048d29b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -26,11 +26,9 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,7 +37,7 @@ import org.junit.runners.Parameterized
/**
* Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -84,8 +82,6 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun displayEndsAt90Degrees() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
hasRotation(Surface.ROTATION_90)
}
@@ -93,41 +89,23 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
- override fun navBarLayerIsVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.navBarLayerIsVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- override fun statusBarLayerIsVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerIsVisible()
- }
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
- super.navBarLayerRotatesAndScales()
- }
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Presubmit
@Test
fun pipWindowInsideDisplay() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmStart {
frameRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -136,8 +114,6 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipAppShowsOnTop() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
isAppWindowOnTop(pipApp.component)
}
@@ -146,8 +122,6 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipLayerInsideDisplay() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -156,8 +130,6 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipAlwaysVisible() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(pipApp.component)
}
@@ -166,8 +138,6 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
- // This test doesn't work in shell transitions because of b/208576418
- assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversExactly(endingBounds)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 2cdbffa7589c..f40aa66932cd 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -25,6 +25,7 @@
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:launchMode="singleTop"
+ android:theme="@style/CutoutShortEdges"
android:label="FixedApp"
android:exported="true">
<intent-filter>
@@ -37,6 +38,7 @@
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
+ android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop"
android:label="PipApp"
android:exported="true">
@@ -52,6 +54,7 @@
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:launchMode="singleTop"
android:exported="true">
@@ -68,6 +71,7 @@
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryApp"
android:exported="true">
<intent-filter>
@@ -79,6 +83,7 @@
<activity android:name=".SplitScreenSecondaryActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryApp"
android:exported="true">
<intent-filter>
@@ -90,6 +95,7 @@
<activity android:name=".NonResizeableActivity"
android:resizeableActivity="false"
android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="NonResizeableApp"
android:exported="true">
<intent-filter>
@@ -100,6 +106,7 @@
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
<intent-filter>
@@ -111,6 +118,7 @@
android:name=".LaunchBubbleActivity"
android:label="LaunchBubbleApp"
android:exported="true"
+ android:theme="@style/CutoutShortEdges"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -121,6 +129,7 @@
android:name=".BubbleActivity"
android:label="BubbleApp"
android:exported="false"
+ android:theme="@style/CutoutShortEdges"
android:resizeableActivity="true" />
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 000000000000..87a61a88c094
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <style name="CutoutDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 16bc50750e1d..636e875bed7e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -46,6 +46,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -86,6 +87,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -139,6 +142,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedTransitionState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 1d92a48c25fd..df21163c68cd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -50,6 +50,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -99,6 +100,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
ShellExecutor mMockShellMainExecutor;
@Mock
OneHandedSettingsUtil mMockSettingsUitl;
+ @Mock
+ InteractionJankMonitor mJankMonitor;
List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
@@ -141,6 +144,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
@@ -428,6 +432,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index bea69c5d80ef..58399b6444fa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -40,6 +40,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -78,6 +79,8 @@ public class OneHandedStateTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -128,6 +131,7 @@ public class OneHandedStateTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index b8029087cb4f..e359145feef7 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -95,6 +95,16 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
endHyphen, advances);
}
+minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+ const Typeface* typeface, const uint16_t* buf,
+ size_t start, size_t count, size_t bufSize) {
+ minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+
+ return minikin::getFontExtent(textBuf, range, bidiFlags, minikinPaint);
+}
+
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index a15803ad2dca..009b84b140ea 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -56,6 +56,10 @@ public:
size_t start, size_t count, size_t bufSize,
float* advances);
+ static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+ const Typeface* typeface, const uint16_t* buf,
+ size_t start, size_t count, size_t bufSize);
+
static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint,
uint32_t vs);
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 22a1e1fd94b9..f76863255153 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -541,26 +541,6 @@ namespace PaintGlue {
return result;
}
- // ------------------ @FastNative ---------------------------
-
- static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- ScopedUtfChars localesChars(env, locales);
- jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
- obj->setMinikinLocaleListId(minikinLocaleListId);
- return minikinLocaleListId;
- }
-
- static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- if (!settings) {
- paint->setFontFeatureSettings(std::string());
- } else {
- ScopedUtfChars settingsChars(env, settings);
- paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
- }
- }
-
static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
@@ -593,6 +573,67 @@ namespace PaintGlue {
return spacing;
}
+ static void doFontExtent(JNIEnv* env, jlong paintHandle, const jchar buf[], jint start,
+ jint count, jint bufSize, jboolean isRtl, jobject fmi) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ minikin::MinikinExtent extent =
+ MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize);
+
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+
+ metrics.fAscent = extent.ascent;
+ metrics.fDescent = extent.descent;
+
+ // If top/bottom is narrower than ascent/descent, adjust top/bottom to ascent/descent.
+ metrics.fTop = std::min(metrics.fAscent, metrics.fTop);
+ metrics.fBottom = std::max(metrics.fDescent, metrics.fBottom);
+
+ GraphicsJNI::set_metrics_int(env, fmi, metrics);
+ }
+
+ static void getFontMetricsIntForText___C(JNIEnv* env, jclass, jlong paintHandle,
+ jcharArray text, jint start, jint count, jint ctxStart,
+ jint ctxCount, jboolean isRtl, jobject fmi) {
+ ScopedCharArrayRO textArray(env, text);
+
+ doFontExtent(env, paintHandle, textArray.get() + ctxStart, start - ctxStart, count,
+ ctxCount, isRtl, fmi);
+ }
+
+ static void getFontMetricsIntForText___String(JNIEnv* env, jclass, jlong paintHandle,
+ jstring text, jint start, jint count,
+ jint ctxStart, jint ctxCount, jboolean isRtl,
+ jobject fmi) {
+ ScopedStringChars textChars(env, text);
+
+ doFontExtent(env, paintHandle, textChars.get() + ctxStart, start - ctxStart, count,
+ ctxCount, isRtl, fmi);
+ }
+
+ // ------------------ @FastNative ---------------------------
+
+ static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
+ obj->setMinikinLocaleListId(minikinLocaleListId);
+ return minikinLocaleListId;
+ }
+
+ static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jstring settings) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (!settings) {
+ paint->setFontFeatureSettings(std::string());
+ } else {
+ ScopedUtfChars settingsChars(env, settings);
+ paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ }
+ }
+
static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
@@ -1015,6 +1056,11 @@ static const JNINativeMethod methods[] = {
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I",
(void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
+ {"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+ (void*)PaintGlue::getFontMetricsIntForText___C},
+ {"nGetFontMetricsIntForText",
+ "(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
+ (void*)PaintGlue::getFontMetricsIntForText___String},
// --------------- @FastNative ----------------------
@@ -1093,6 +1139,7 @@ static const JNINativeMethod methods[] = {
{"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
+
int register_android_graphics_Paint(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 61caa0bf4660..9109a18f120e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -3640,7 +3640,7 @@ public class LocationManager {
}
@Override
- protected Boolean recompute(Integer userId) {
+ public Boolean recompute(Integer userId) {
Preconditions.checkArgument(userId >= 0);
if (mManager == null) {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f9fc17fdb472..45c2a5a90c70 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -283,9 +283,21 @@ public class Filter implements AutoCloseable {
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onFilterEvent(this, events);
+ } else {
+ for (FilterEvent event : events) {
+ if (event instanceof MediaEvent) {
+ ((MediaEvent)event).release();
+ }
+ }
}
}
});
+ } else {
+ for (FilterEvent event : events) {
+ if (event instanceof MediaEvent) {
+ ((MediaEvent)event).release();
+ }
+ }
}
}
}
@@ -558,6 +570,8 @@ public class Filter implements AutoCloseable {
if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "Failed to close filter.");
} else {
+ mCallback = null;
+ mExecutor = null;
mIsStarted = false;
mIsClosed = true;
}
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 38bac1c236a9..d3d8bba16c7c 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -25,15 +25,21 @@ filegroup {
name: "framework-connectivity-netstats-internal-sources",
srcs: [
"src/android/app/usage/*.java",
- "src/android/net/DataUsage*.*",
- "src/android/net/INetworkStats*.*",
- "src/android/net/NetworkIdentity*.java",
+ "src/android/net/DataUsageRequest.*",
+ "src/android/net/INetworkStatsService.aidl",
+ "src/android/net/INetworkStatsSession.aidl",
+ "src/android/net/NetworkIdentity.java",
+ "src/android/net/NetworkIdentitySet.java",
"src/android/net/NetworkStateSnapshot.*",
- "src/android/net/NetworkStats*.*",
+ "src/android/net/NetworkStats.*",
+ "src/android/net/NetworkStatsAccess.*",
+ "src/android/net/NetworkStatsCollection.*",
+ "src/android/net/NetworkStatsHistory.*",
"src/android/net/NetworkTemplate.*",
"src/android/net/TrafficStats.java",
"src/android/net/UnderlyingNetworkInfo.*",
"src/android/net/netstats/**/*.*",
+ "src/com/android/server/NetworkManagementSocketTagger.java",
],
path: "src",
visibility: [
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index a84e7a9c6344..10a22ac360b1 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -343,7 +343,7 @@ public final class IpSecAlgorithm implements Parcelable {
// Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
// the resource are not allowed.
final String[] resourceAlgos = systemResources.getStringArray(
- com.android.internal.R.array.config_optionalIpSecAlgorithms);
+ android.R.array.config_optionalIpSecAlgorithms);
for (String str : resourceAlgos) {
if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
// This error should be caught by CTS and never be thrown to API callers
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
index d89566c9119c..e35f6a648b77 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -18,7 +18,6 @@ package com.android.server;
import android.os.StrictMode;
import android.util.Log;
-import android.util.Slog;
import dalvik.system.SocketTagger;
@@ -122,7 +121,7 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
public static void resetKernelUidStats(int uid) {
int errno = native_deleteTagData(0, uid);
if (errno < 0) {
- Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
+ Log.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
}
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index ced2e22f149d..97281ed42452 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -448,7 +448,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- mHandler.getLooper(), new HandlerExecutor(mHandler), this);
+ new HandlerExecutor(mHandler), this);
mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor);
@@ -474,11 +474,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/
@NonNull
public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
- @NonNull Looper looper, @NonNull Executor executor,
- @NonNull NetworkStatsService service) {
+ @NonNull Executor executor, @NonNull NetworkStatsService service) {
// TODO: Update RatType passively in NSS, instead of querying into the monitor
// when notifyNetworkStatus.
- return new NetworkStatsSubscriptionsMonitor(context, looper, executor,
+ return new NetworkStatsSubscriptionsMonitor(context, executor,
(subscriberId, type) -> service.handleOnCollapsedRatTypeChanged());
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 9bb7bb80782b..6df6de32da7a 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -21,7 +21,6 @@ import static android.net.NetworkTemplate.getCollapsedRatType;
import android.annotation.NonNull;
import android.content.Context;
-import android.os.Looper;
import android.telephony.Annotation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -79,9 +78,9 @@ public class NetworkStatsSubscriptionsMonitor extends
@NonNull
private final Executor mExecutor;
- NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper,
+ NetworkStatsSubscriptionsMonitor(@NonNull Context context,
@NonNull Executor executor, @NonNull Delegate delegate) {
- super(looper);
+ super();
mSubscriptionManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 2b357c57b306..1e8cb9fc4622 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -38,6 +38,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
+import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -54,6 +55,7 @@ import java.util.List;
public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
private static final String LOG_TAG = "RestrictedLockUtils";
+ private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
/**
* @return drawables for displaying with settings that are locked by a device admin.
@@ -92,14 +94,25 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
}
final UserManager um = UserManager.get(context);
+ final UserHandle userHandle = UserHandle.of(userId);
final List<UserManager.EnforcingUser> enforcingUsers =
- um.getUserRestrictionSources(userRestriction, UserHandle.of(userId));
+ um.getUserRestrictionSources(userRestriction, userHandle);
if (enforcingUsers.isEmpty()) {
// Restriction is not enforced.
return null;
- } else if (enforcingUsers.size() > 1) {
- return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
+ }
+ final int size = enforcingUsers.size();
+ if (size > 1) {
+ final EnforcedAdmin enforcedAdmin = EnforcedAdmin
+ .createDefaultEnforcedAdminWithRestriction(userRestriction);
+ enforcedAdmin.user = userHandle;
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Multiple (" + size + ") enforcing users for restriction '"
+ + userRestriction + "' on user " + userHandle + "; returning default admin "
+ + "(" + enforcedAdmin + ")");
+ }
+ return enforcedAdmin;
}
final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 16cece93f2de..8e35ee96b691 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@ public class SecureSettings {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 13c1e512cb14..231252502937 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -267,6 +267,7 @@ public class SecureSettingsValidators {
new InclusiveIntegerRangeValidator(
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL));
+ VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cd6447fae8cd..00cdc9b0a058 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,10 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
p.end(accessibilityToken);
final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b19ef3a243aa..c805e2d0330a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -138,6 +138,7 @@ filegroup {
"tests/src/com/android/systemui/statusbar/RankingBuilder.java",
"tests/src/com/android/systemui/statusbar/SbnBuilder.java",
"tests/src/com/android/systemui/SysuiTestableContext.java",
+ "tests/src/com/android/systemui/util/**/*Fake.java",
"tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java",
"tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java",
"tests/src/com/android/systemui/**/Fake*.java",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 092758ee34ed..dee4ff5a0bf5 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -15,12 +15,6 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.helpers.Staging"
- },
- {
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
},
{
@@ -99,9 +93,6 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
- "exclude-annotation": "android.platform.helpers.Staging"
- },
- {
"exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
},
{
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 0b3eccfd3a91..29221aa04699 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -28,18 +28,88 @@ import kotlin.math.roundToInt
const val TAG = "ColorScheme"
const val ACCENT1_CHROMA = 48.0f
-const val ACCENT2_CHROMA = 16.0f
-const val ACCENT3_CHROMA = 32.0f
-const val ACCENT3_HUE_SHIFT = 60.0f
+const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
+const val MIN_CHROMA = 5
-const val NEUTRAL1_CHROMA = 4.0f
-const val NEUTRAL2_CHROMA = 8.0f
+internal enum class ChromaStrategy {
+ EQ, GTE
+}
-const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
+internal enum class HueStrategy {
+ SOURCE, ADD, SUBTRACT
+}
-const val MIN_CHROMA = 5
+internal class Chroma(val strategy: ChromaStrategy, val value: Double) {
+ fun get(sourceChroma: Double): Double {
+ return when (strategy) {
+ ChromaStrategy.EQ -> value
+ ChromaStrategy.GTE -> sourceChroma.coerceAtLeast(value)
+ }
+ }
+}
+
+internal class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) {
+ fun get(sourceHue: Double): Double {
+ return when (strategy) {
+ HueStrategy.SOURCE -> sourceHue
+ HueStrategy.ADD -> ColorScheme.wrapDegreesDouble(sourceHue + value)
+ HueStrategy.SUBTRACT -> ColorScheme.wrapDegreesDouble(sourceHue - value)
+ }
+ }
+}
+
+internal class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) {
+ fun shades(sourceColor: Cam): List<Int> {
+ val hue = hue.get(sourceColor.hue.toDouble())
+ val chroma = chroma.get(sourceColor.chroma.toDouble())
+ return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
+ }
+}
-public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
+internal class CoreSpec(
+ val a1: TonalSpec,
+ val a2: TonalSpec,
+ val a3: TonalSpec,
+ val n1: TonalSpec,
+ val n2: TonalSpec
+)
+
+enum class Style(internal val coreSpec: CoreSpec) {
+ SPRITZ(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0))
+ )),
+ TONAL_SPOT(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+ a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0))
+ )),
+ VIBRANT(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(Hue(HueStrategy.ADD, 10.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ a3 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.GTE, 32.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ )),
+ EXPRESSIVE(CoreSpec(
+ a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 40.0), Chroma(ChromaStrategy.GTE, 64.0)),
+ a2 = TonalSpec(Hue(HueStrategy.ADD, 20.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ a3 = TonalSpec(Hue(HueStrategy.SUBTRACT, 80.0), Chroma(ChromaStrategy.GTE, 64.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0))
+ )),
+}
+
+class ColorScheme(
+ @ColorInt seed: Int,
+ val darkTheme: Boolean,
+ val style: Style = Style.TONAL_SPOT
+) {
val accent1: List<Int>
val accent2: List<Int>
@@ -47,6 +117,9 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
val neutral1: List<Int>
val neutral2: List<Int>
+ constructor(@ColorInt seed: Int, darkTheme: Boolean):
+ this(seed, darkTheme, Style.TONAL_SPOT)
+
constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
this(getSeedColor(wallpaperColors), darkTheme)
@@ -83,14 +156,11 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
seed
}
val camSeed = Cam.fromInt(seedArgb)
- val hue = camSeed.hue
- val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
- val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt())
- accent1 = Shades.of(hue, chroma).toList()
- accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
- accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList()
- neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
- neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
+ accent1 = style.coreSpec.a1.shades(camSeed)
+ accent2 = style.coreSpec.a2.shades(camSeed)
+ accent3 = style.coreSpec.a3.shades(camSeed)
+ neutral1 = style.coreSpec.n1.shades(camSeed)
+ neutral2 = style.coreSpec.n2.shades(camSeed)
}
override fun toString(): String {
@@ -100,6 +170,7 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
" accent1: ${humanReadable(accent1)}\n" +
" accent2: ${humanReadable(accent2)}\n" +
" accent3: ${humanReadable(accent3)}\n" +
+ " style: $style\n" +
"}"
}
@@ -225,6 +296,20 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
}
}
+ public fun wrapDegreesDouble(degrees: Double): Double {
+ return when {
+ degrees < 0 -> {
+ (degrees % 360) + 360
+ }
+ degrees >= 360 -> {
+ degrees % 360
+ }
+ else -> {
+ degrees
+ }
+ }
+ }
+
private fun hueDiff(a: Float, b: Float): Float {
return 180f - ((a - b).absoluteValue - 180f).absoluteValue
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index ffac26b2e272..1ef532407761 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -159,9 +159,9 @@ public interface QSTile {
public Supplier<Icon> iconSupplier;
public int state = DEFAULT_STATE;
public CharSequence label;
- public CharSequence secondaryLabel;
+ @Nullable public CharSequence secondaryLabel;
public CharSequence contentDescription;
- public CharSequence stateDescription;
+ @Nullable public CharSequence stateDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
public boolean dualTarget = false;
@@ -170,6 +170,7 @@ public interface QSTile {
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
+ @Nullable
public Drawable sideViewCustomDrawable;
public String spec;
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
new file mode 100644
index 000000000000..f898ef65213a
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_complications_layer"
+ android:padding="20dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextClock
+ android:id="@+id/time_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-thin"
+ android:format12Hour="h:mm"
+ android:format24Hour="kk:mm"
+ android:shadowColor="#B2000000"
+ android:shadowRadius="2.0"
+ android:singleLine="true"
+ android:textSize="72sp"
+ app:layout_constraintBottom_toTopOf="@+id/date_view"
+ app:layout_constraintStart_toStartOf="parent" />
+ <TextClock
+ android:id="@+id/date_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:shadowColor="#B2000000"
+ android:shadowRadius="2.0"
+ android:format12Hour="EEE, MMM d"
+ android:format24Hour="EEE, MMM d"
+ android:singleLine="true"
+ android:textSize="18sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@+id/time_view"
+ app:layout_constraintStart_toStartOf="@+id/time_view" />
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4929f502fef0..c6b502ec91b0 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -28,6 +28,8 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
+ <include layout="@layout/dream_overlay_complications_layer" />
+
<com.android.systemui.dreams.DreamOverlayStatusBarView
android:id="@+id/dream_overlay_status_bar"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_container.xml
index 63a878f772f9..c717e3756f3d 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_container.xml
@@ -16,7 +16,7 @@
-->
<!-- Layout for media controls on the lockscreen -->
-<com.android.systemui.statusbar.notification.stack.MediaHeaderView
+<com.android.systemui.statusbar.notification.stack.MediaContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2bf121d2e9ab..2916c1c9b357 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1165,10 +1165,10 @@
<string name="wallet_lockscreen_settings_label">Lock screen settings</string>
<!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
- <string name="qr_code_scanner_title">Scan QR</string>
+ <string name="qr_code_scanner_title">QR Code</string>
<!-- QR Code Scanner description [CHAR LIMIT=NONE] -->
- <string name="qr_code_scanner_description">Click to scan a QR code</string>
+ <string name="qr_code_scanner_description">Tap to scan</string>
<!-- Name of the work status bar icon. -->
<string name="status_bar_work">Work profile</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 24e93efa09cb..953b0e018306 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -21,24 +21,24 @@ import android.content.Context
import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
-import java.lang.IllegalStateException
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import java.util.concurrent.Executor
/**
* Factory for [UnfoldTransitionProgressProvider].
*
- * This is needed as Launcher has to create the object manually.
- * Sysui create it using dagger (see [UnfoldTransitionModule]).
+ * This is needed as Launcher has to create the object manually. Sysui create it using dagger (see
+ * [UnfoldTransitionModule]).
*/
fun createUnfoldTransitionProgressProvider(
context: Context,
@@ -52,10 +52,45 @@ fun createUnfoldTransitionProgressProvider(
): UnfoldTransitionProgressProvider {
if (!config.isEnabled) {
- throw IllegalStateException("Trying to create " +
- "UnfoldTransitionProgressProvider when the transition is disabled")
+ throw IllegalStateException(
+ "Trying to create " +
+ "UnfoldTransitionProgressProvider when the transition is disabled")
}
+ val foldStateProvider =
+ createFoldStateProvider(
+ context,
+ config,
+ screenStatusProvider,
+ deviceStateManager,
+ sensorManager,
+ mainHandler,
+ mainExecutor)
+
+ val unfoldTransitionProgressProvider =
+ if (config.isHingeAngleEnabled) {
+ PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
+ } else {
+ FixedTimingTransitionProgressProvider(foldStateProvider)
+ }
+
+ return ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider, context.contentResolver)
+ .apply {
+ // Always present callback that logs animation beginning and end.
+ addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
+ }
+}
+
+fun createFoldStateProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: ScreenStatusProvider,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ mainHandler: Handler,
+ mainExecutor: Executor
+): FoldStateProvider {
val hingeAngleProvider =
if (config.isHingeAngleEnabled) {
HingeSensorAngleProvider(sensorManager)
@@ -63,28 +98,13 @@ fun createUnfoldTransitionProgressProvider(
EmptyHingeAngleProvider()
}
- val foldStateProvider = DeviceFoldStateProvider(
+ return DeviceFoldStateProvider(
context,
hingeAngleProvider,
screenStatusProvider,
deviceStateManager,
mainExecutor,
- mainHandler
- )
-
- val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
- PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
- } else {
- FixedTimingTransitionProgressProvider(foldStateProvider)
- }
- return ScaleAwareTransitionProgressProvider(
- unfoldTransitionProgressProvider,
- context.contentResolver
- ).apply {
- // Always present callback that logs animation beginning and end.
- addCallback(ATraceLoggerTransitionProgressListener(tracingTagPrefix))
- }
+ mainHandler)
}
-fun createConfig(context: Context): UnfoldTransitionConfig =
- ResourceUnfoldTransitionConfig(context)
+fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index dc64f14b830d..a701b44cf916 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -23,7 +23,7 @@ import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.updates.FOLD_UPDATE_ABORTED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
@@ -84,7 +84,7 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
cancelTransition(endValue = 1f, animate = true)
}
}
- FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_ABORTED -> {
+ FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> {
// Do not cancel if we haven't started the transition yet.
// This could happen when we fully unfolded the device before the screen
// became available. In this case we start and immediately cancel the animation
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 6d9631c12430..cd1ea215ccdd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -138,7 +138,7 @@ class DeviceFoldStateProvider(
if (isTransitionInProgess) {
cancelTimeout()
}
- handler.postDelayed(timeoutRunnable, ABORT_CLOSING_MILLIS)
+ handler.postDelayed(timeoutRunnable, HALF_OPENED_TIMEOUT_MILLIS)
}
private fun cancelTimeout() {
@@ -163,16 +163,14 @@ class DeviceFoldStateProvider(
}
private inner class HingeAngleListener : Consumer<Float> {
-
override fun accept(angle: Float) {
onHingeAngle(angle)
}
}
private inner class TimeoutRunnable : Runnable {
-
override fun run() {
- notifyFoldUpdate(FOLD_UPDATE_ABORTED)
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
}
}
}
@@ -180,9 +178,7 @@ class DeviceFoldStateProvider(
private fun stateToString(@FoldUpdate update: Int): String {
return when (update) {
FOLD_UPDATE_START_OPENING -> "START_OPENING"
- FOLD_UPDATE_HALF_OPEN -> "HALF_OPEN"
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
- FOLD_UPDATE_ABORTED -> "ABORTED"
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN"
FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN"
@@ -195,11 +191,11 @@ private const val TAG = "DeviceFoldProvider"
private const val DEBUG = false
/**
- * Time after which [FOLD_UPDATE_ABORTED] is emitted following a [FOLD_UPDATE_START_CLOSING] or
- * [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
+ * Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
+ * [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
*/
@VisibleForTesting
-const val ABORT_CLOSING_MILLIS = 1000L
+const val HALF_OPENED_TIMEOUT_MILLIS = 1000L
/** Threshold after which we consider the device fully unfolded. */
@VisibleForTesting
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index bffebcd4512b..df3563df5fc6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -37,9 +37,7 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
@IntDef(prefix = ["FOLD_UPDATE_"], value = [
FOLD_UPDATE_START_OPENING,
- FOLD_UPDATE_HALF_OPEN,
FOLD_UPDATE_START_CLOSING,
- FOLD_UPDATE_ABORTED,
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
FOLD_UPDATE_FINISH_HALF_OPEN,
FOLD_UPDATE_FINISH_FULL_OPEN,
@@ -50,10 +48,8 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
}
const val FOLD_UPDATE_START_OPENING = 0
-const val FOLD_UPDATE_HALF_OPEN = 1
-const val FOLD_UPDATE_START_CLOSING = 2
-const val FOLD_UPDATE_ABORTED = 3
-const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 4
-const val FOLD_UPDATE_FINISH_HALF_OPEN = 5
-const val FOLD_UPDATE_FINISH_FULL_OPEN = 6
-const val FOLD_UPDATE_FINISH_CLOSED = 7
+const val FOLD_UPDATE_START_CLOSING = 1
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 3
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 4
+const val FOLD_UPDATE_FINISH_CLOSED = 5
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index a10efa982701..4784bc12099b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -225,6 +225,13 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
}
@Override
+ public void onDrag(int displayId) {
+ if (mWindowMagnificationConnectionImpl != null) {
+ mWindowMagnificationConnectionImpl.onDrag(displayId);
+ }
+ }
+
+ @Override
public void requestWindowMagnificationConnection(boolean connect) {
if (connect) {
setWindowMagnificationConnection();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 2133da202ce9..1d22633455e9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -142,4 +142,14 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
}
}
+
+ void onDrag(int displayId) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onDrag(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform taking control by a user", e);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index b064ba904120..aa1a43397f65 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -252,7 +252,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mMagnificationFrame.height());
mTransaction.setGeometry(mMirrorSurface, mSourceBounds, mTmpRect,
Surface.ROTATION_0).apply();
- mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
+
+ // Notify source bounds change when the magnifier is not animating.
+ if (!mAnimationController.isAnimating()) {
+ mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId,
+ mSourceBounds);
+ }
}
};
mUpdateStateDescriptionRunnable = () -> {
@@ -596,7 +601,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private void modifyWindowMagnification(SurfaceControl.Transaction t) {
mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
updateMirrorViewLayout();
-
}
/**
@@ -800,7 +804,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* are as same as current values, or the transition is interrupted
* due to the new transition request.
*/
- void enableWindowMagnification(float scale, float centerX, float centerY,
+ public void enableWindowMagnification(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
mAnimationController.enableWindowMagnification(scale, centerX, centerY,
@@ -960,6 +964,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@Override
public boolean onDrag(float offsetX, float offsetY) {
moveWindowMagnifier(offsetX, offsetY);
+ mWindowMagnifierCallback.onDrag(mDisplayId);
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index 628a5e88b89e..bdded10dfa1d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import android.graphics.Rect;
+import android.view.ViewConfiguration;
/**
* A callback to inform {@link com.android.server.accessibility.AccessibilityManagerService} about
@@ -53,4 +54,13 @@ interface WindowMagnifierCallback {
* @param displayId The logical display id.
*/
void onAccessibilityActionPerformed(int displayId);
+
+ /**
+ * Called when the user is performing dragging gesture. It is started after the offset
+ * between the down location and the move event location exceed
+ * {@link ViewConfiguration#getScaledTouchSlop()}.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDrag(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
new file mode 100644
index 000000000000..08d969b5eb77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LSShadeTransitionLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for lockscreen to shade transition events. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface LSShadeTransitionLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 1f953d7ee6c9..b32358643efb 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -61,6 +61,14 @@ public class LogModule {
return factory.create("NotifHeadsUpLog", 1000);
}
+ /** Provides a logging buffer for all logs for lockscreen to shade transition events. */
+ @Provides
+ @SysUISingleton
+ @LSShadeTransitionLog
+ public static LogBuffer provideLSShadeTransitionControllerBuffer(LogBufferFactory factory) {
+ return factory.create("LSShadeTransitionLog", 50);
+ }
+
/** Provides a logging buffer for all logs related to managing notification sections. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 5ff624db33c7..44727f29888d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -27,7 +27,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
@@ -96,14 +96,14 @@ class KeyguardMediaController @Inject constructor(
/**
* single pane media container placed at the top of the notifications list
*/
- var singlePaneContainer: MediaHeaderView? = null
+ var singlePaneContainer: MediaContainerView? = null
private set
private var splitShadeContainer: ViewGroup? = null
/**
* Attaches media container in single pane mode, situated at the top of the notifications list
*/
- fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
+ fun attachSinglePaneContainer(mediaView: MediaContainerView?) {
val needsListener = singlePaneContainer == null
singlePaneContainer = mediaView
if (needsListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ce3b443e677e..29321b46328a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -865,6 +865,10 @@ class MediaCarouselController @Inject constructor(
println("playerKeys: ${MediaPlayerData.playerKeys()}")
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
+ println("current size: $currentCarouselWidth x $currentCarouselHeight")
+ println("location: $desiredLocation")
+ println("state: ${desiredHostState?.expansion}, " +
+ "only active ${desiredHostState?.showsOnlyActiveMedia}")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9c4227e25567..d190dcb3ffb8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -227,6 +227,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private @Behavior int mBehavior;
private boolean mTransientShown;
+ private boolean mTransientShownFromGestureOnSystemBar;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private LightBarController mLightBarController;
private final LightBarController mMainLightBarController;
@@ -871,6 +872,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
+ BarTransitions.modeToString(mNavigationBarMode));
+ pw.println(" mTransientShown=" + mTransientShown);
+ pw.println(" mTransientShownFromGestureOnSystemBar="
+ + mTransientShownFromGestureOnSystemBar);
dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
mNavigationBarView.dump(pw);
}
@@ -989,7 +993,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
@@ -998,6 +1003,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
if (!mTransientShown) {
mTransientShown = true;
+ mTransientShownFromGestureOnSystemBar = isGestureOnSystemBar;
handleTransientChanged();
}
}
@@ -1016,12 +1022,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
+ mTransientShownFromGestureOnSystemBar = false;
handleTransientChanged();
}
}
private void handleTransientChanged() {
- mNavigationBarView.onTransientStateChanged(mTransientShown);
+ mNavigationBarView.onTransientStateChanged(mTransientShown,
+ mTransientShownFromGestureOnSystemBar);
final int barMode = barMode(mTransientShown, mAppearance);
if (updateBarMode(barMode) && mLightBarController != null) {
mLightBarController.onNavigationBarModeChanged(barMode);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 7adb7ac92dc1..5fbdd88b9f66 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -447,12 +447,17 @@ public class NavigationBarView extends FrameLayout implements
mRegionSamplingHelper.setWindowHasBlurs(hasBlurs);
}
- void onTransientStateChanged(boolean isTransient) {
+ void onTransientStateChanged(boolean isTransient, boolean isGestureOnSystemBar) {
mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient);
// The visibility of the navigation bar buttons is dependent on the transient state of
// the navigation bar.
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ // Always allow the overlay if in non-gestural nav mode, otherwise, only allow showing
+ // the overlay if the user is swiping directly over a system bar
+ boolean allowNavBarOverlay = !QuickStepContract.isGesturalMode(mNavBarMode)
+ || isGestureOnSystemBar;
+ isTransient = isTransient && allowNavBarOverlay;
mNavBarOverlayController.setButtonState(isTransient, /* force */ false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index feda99fd3471..002dd10f7356 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -19,7 +19,7 @@ package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -86,6 +86,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
+ private final NavigationBarOverlayController mNavBarOverlayController;
private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
@@ -140,6 +141,13 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void hide() {
+ clearTransient();
+ }
+ };
+
+ private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
+ if (visible) {
+ mAutoHideController.touchAutoHide();
}
};
@@ -147,6 +155,11 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
public TaskbarDelegate(Context context) {
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(context);
+ mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback,
+ mEdgeBackGestureHandler::updateNavigationBarOverlayExcludeRegion);
+ }
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
@@ -206,6 +219,9 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mNavigationModeController.addListener(this));
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.init();
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.registerListeners();
+ }
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -229,6 +245,9 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mNavigationModeController.removeListener(this);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.unregisterListeners();
+ }
mEdgeBackGestureHandler.onNavBarDetached();
mScreenPinningNotify = null;
if (mWindowContext != null) {
@@ -350,14 +369,17 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
@Override
- public void showTransient(int displayId, int[] types) {
+ public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
- if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
return;
}
- mTaskbarTransientShowing = true;
+ if (!mTaskbarTransientShowing) {
+ mTaskbarTransientShowing = true;
+ onTransientStateChanged();
+ }
}
@Override
@@ -365,15 +387,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
if (displayId != mDisplayId) {
return;
}
- if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ if (!containsType(types, ITYPE_EXTRA_NAVIGATION_BAR)) {
return;
}
- mTaskbarTransientShowing = false;
+ clearTransient();
}
@Override
public void onTaskbarAutohideSuspend(boolean suspend) {
- mTaskbarTransientShowing = suspend;
if (suspend) {
mAutoHideController.suspendAutoHide();
} else {
@@ -381,6 +402,30 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
}
+ private void clearTransient() {
+ if (mTaskbarTransientShowing) {
+ mTaskbarTransientShowing = false;
+ onTransientStateChanged();
+ }
+ }
+
+ private void onTransientStateChanged() {
+ mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTaskbarTransientShowing);
+
+ // The visibility of the navigation bar buttons is dependent on the transient state of
+ // the navigation bar.
+ if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
+ mNavBarOverlayController.setButtonState(mTaskbarTransientShowing, /* force */ false);
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationStateChanged(boolean running) {
+ if (running) {
+ mNavBarOverlayController.setButtonState(/* visible */false, /* force */true);
+ }
+ }
+
@Override
public void onNavigationModeChanged(int mode) {
mNavigationMode = mode;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
index 1195184050e3..18d28bf511bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
@@ -36,6 +36,7 @@ public class AutoSizingList extends LinearLayout {
private final int mItemSize;
private final Handler mHandler;
+ @Nullable
private ListAdapter mAdapter;
private int mCount;
private boolean mEnableAutoSizing;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 6d1f8f7d01b1..d20141b33838 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -19,6 +19,7 @@ import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.Scroller;
+import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -51,14 +52,17 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TileLayout> mPages = new ArrayList<>();
+ @Nullable
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
+ @Nullable
private PageListener mPageListener;
private boolean mListening;
private Scroller mScroller;
+ @Nullable
private AnimatorSet mBounceAnimatorSet;
private float mLastExpansion;
private boolean mDistributeTiles = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 6c7d6e01e9ec..e06b768dbe6f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -26,6 +26,8 @@ import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
@@ -88,6 +90,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private final View mQSFooterActions;
private final View mQQSFooterActions;
+ @Nullable
private PagedTileLayout mPagedLayout;
private boolean mOnFirstPage = true;
@@ -95,6 +98,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
// Animator for elements in the first page, including secondary labels and qqs brightness
// slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
+ @Nullable
private TouchAnimator mFirstPageAnimator;
// TranslationX animator for QQS/QS tiles
private TouchAnimator mTranslationXAnimator;
@@ -109,13 +113,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
// Animator for brightness slider(s)
+ @Nullable
private TouchAnimator mBrightnessAnimator;
// Animator for Footer actions in QQS
private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
+ @Nullable
private HeightExpansionAnimator mQQSTileHeightAnimator;
// Height animator for QS tile in first page but not in QQS, to present the illusion that they
// are expanding alongside the QQS tiles
+ @Nullable
private HeightExpansionAnimator mOtherFirstPageTilesHeightAnimator;
// Pair of animators for each non first page. The creation is delayed until the user first
// scrolls to that page, in order to get the proper measures and layout.
@@ -215,7 +222,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
}
@Override
- public void onViewAttachedToWindow(View v) {
+ public void onViewAttachedToWindow(@Nullable View v) {
mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
MOVE_FULL_ROWS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index d43404b8781c..04e22522bcd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -64,15 +64,18 @@ public class QSDetail extends LinearLayout {
protected TextView mDetailDoneButton;
@VisibleForTesting
QSDetailClipper mClipper;
+ @Nullable
private DetailAdapter mDetailAdapter;
private QSPanelController mQsPanelController;
protected View mQsDetailHeader;
protected TextView mQsDetailHeaderTitle;
private ViewStub mQsDetailHeaderSwitchStub;
+ @Nullable
private Switch mQsDetailHeaderSwitch;
protected ImageView mQsDetailHeaderProgress;
+ @Nullable
protected QSTileHost mHost;
private boolean mScanState;
@@ -87,6 +90,7 @@ public class QSDetail extends LinearLayout {
private boolean mSwitchState;
private QSFooter mFooter;
+ @Nullable
private QSContainerController mQsContainerController;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
@@ -183,12 +187,14 @@ public class QSDetail extends LinearLayout {
}
public interface Callback {
- void onShowingDetail(DetailAdapter detail, int x, int y);
+ /** Handle an event of showing detail. */
+ void onShowingDetail(@Nullable DetailAdapter detail, int x, int y);
void onToggleStateChanged(boolean state);
void onScanStateChanged(boolean state);
}
- public void handleShowingDetail(final DetailAdapter adapter, int x, int y,
+ /** Handle an event of showing detail. */
+ public void handleShowingDetail(final @Nullable DetailAdapter adapter, int x, int y,
boolean toggleQs) {
final boolean showingDetail = adapter != null;
final boolean wasShowingDetail = mDetailAdapter != null;
@@ -378,7 +384,8 @@ public class QSDetail extends LinearLayout {
}
@Override
- public void onShowingDetail(final DetailAdapter detail, final int x, final int y) {
+ public void onShowingDetail(
+ final @Nullable DetailAdapter detail, final int x, final int y) {
post(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index 63cedd081fb1..43136d32c68f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -23,12 +23,15 @@ import android.graphics.drawable.TransitionDrawable;
import android.view.View;
import android.view.ViewAnimationUtils;
+import androidx.annotation.Nullable;
+
/** Helper for quick settings detail panel clip animations. **/
public class QSDetailClipper {
private final View mDetail;
private final TransitionDrawable mBackground;
+ @Nullable
private Animator mAnimator;
public QSDetailClipper(View detail) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
index b50af004aff9..afd4f0f7d1e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -26,13 +28,14 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class QSDetailDisplayer {
+ @Nullable
private QSPanelController mQsPanelController;
@Inject
public QSDetailDisplayer() {
}
- public void setQsPanelController(QSPanelController qsPanelController) {
+ public void setQsPanelController(@Nullable QSPanelController qsPanelController) {
mQsPanelController = qsPanelController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index e93c349ee41f..eb3247b53823 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -33,6 +33,8 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
@@ -50,6 +52,7 @@ public class QSDetailItems extends FrameLayout {
private final Adapter mAdapter = new Adapter();
private String mTag;
+ @Nullable
private Callback mCallback;
private boolean mItemsVisible = true;
private AutoSizingList mItemList;
@@ -130,7 +133,8 @@ public class QSDetailItems extends FrameLayout {
mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
}
- public void setItems(Item[] items) {
+ /** Set items. */
+ public void setItems(@Nullable Item[] items) {
mHandler.removeMessages(H.SET_ITEMS);
mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
}
@@ -266,9 +270,12 @@ public class QSDetailItems extends FrameLayout {
}
public int iconResId;
+ @Nullable
public QSTile.Icon icon;
+ @Nullable
public Drawable overlay;
public CharSequence line1;
+ @Nullable
public CharSequence line2;
public Object tag;
public boolean canDisconnect;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 4d23958d56ab..066a286b271d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -48,6 +48,7 @@ public class QSFooterView extends FrameLayout {
private TextView mBuildText;
private View mActionsContainer;
+ @Nullable
protected TouchAnimator mFooterAnimator;
private boolean mQsDisabled;
@@ -56,6 +57,7 @@ public class QSFooterView extends FrameLayout {
private boolean mShouldShowBuildText;
+ @Nullable
private OnClickListener mExpandClickListener;
private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bb8a1e8e22a4..41dced6bffeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -117,6 +117,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private QSPanelController mQSPanelController;
private QuickQSPanelController mQuickQSPanelController;
private QSCustomizerController mQSCustomizerController;
+ @Nullable
private ScrollListener mScrollListener;
/**
* When true, QS will translate from outside the screen. It will be clipped with parallax
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ff9790c3595f..d4d6da8f5910 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -401,6 +401,9 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
pw.print(" "); pw.println(record.tileView.toString());
}
}
+ if (mMediaHost != null) {
+ pw.println(" media bounds: " + mMediaHost.getCurrentBounds());
+ }
}
public QSPanel.QSTileLayout getTileLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7f19d0e6c25c..878f7530fa8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -48,6 +48,7 @@ import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
@@ -85,8 +86,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
protected H mHandler;
private boolean mIsVisible;
+ @Nullable
private CharSequence mFooterTextContent = null;
private int mFooterIconId;
+ @Nullable
private Drawable mPrimaryFooterIconDrawable;
@Inject
@@ -240,6 +243,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
mMainHandler.post(mUpdateDisplayState);
}
+ @Nullable
protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
@@ -497,6 +501,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(R.string.ok);
}
+ @Nullable
private String getNegativeButton() {
if (mSecurityController.isParentalControlsEnabled()) {
return mContext.getString(R.string.monitoring_button_view_controls);
@@ -504,6 +509,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return null;
}
+ @Nullable
protected CharSequence getManagementMessage(boolean isDeviceManaged,
CharSequence organizationName) {
if (!isDeviceManaged) {
@@ -521,6 +527,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(R.string.monitoring_description_management);
}
+ @Nullable
protected CharSequence getCaCertsMessage(boolean isDeviceManaged, boolean hasCACerts,
boolean hasCACertsInWorkProfile) {
if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
@@ -534,6 +541,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return mContext.getString(R.string.monitoring_description_ca_certificate);
}
+ @Nullable
protected CharSequence getNetworkLoggingMessage(boolean isDeviceManaged,
boolean isNetworkLoggingEnabled) {
if (!isNetworkLoggingEnabled) return null;
@@ -545,6 +553,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
}
+ @Nullable
protected CharSequence getVpnMessage(boolean isDeviceManaged, boolean hasWorkProfile,
String vpnName, String vpnNameWorkProfile) {
if (vpnName == null && vpnNameWorkProfile == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1030c21ecc79..cca491343f76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -29,6 +29,8 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
@@ -98,6 +100,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final CustomTileStatePersister mCustomTileStatePersister;
private final List<Callback> mCallbacks = new ArrayList<>();
+ @Nullable
private AutoTileManager mAutoTiles;
private final StatusBarIconController mIconController;
private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
@@ -472,6 +475,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
saveTilesToSettings(newTiles);
}
+ /** Create a {@link QSTile} of a {@code tileSpec} type. */
+ @Nullable
public QSTile createTile(String tileSpec) {
for (int i = 0; i < mQsFactories.size(); i++) {
QSTile t = mQsFactories.get(i).createTile(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8b9498394384..866b1b8cabb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,6 +33,7 @@ import android.widget.LinearLayout;
import android.widget.Space;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
import com.android.settingslib.Utils;
@@ -44,7 +45,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconMa
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.VariableDateView;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.util.List;
@@ -57,8 +57,11 @@ public class QuickStatusBarHeader extends FrameLayout {
private boolean mExpanded;
private boolean mQsDisabled;
+ @Nullable
private TouchAnimator mAlphaAnimator;
+ @Nullable
private TouchAnimator mTranslationAnimator;
+ @Nullable
private TouchAnimator mIconsAlphaAnimator;
private TouchAnimator mIconsAlphaAnimatorFixed;
@@ -85,7 +88,9 @@ public class QuickStatusBarHeader extends FrameLayout {
private StatusIconContainer mIconContainer;
private View mPrivacyChip;
+ @Nullable
private TintedIconManager mTintedIconManager;
+ @Nullable
private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private StatusBarContentInsetsProvider mInsetsProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
index bb2340c2cad5..130bcab84f4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickTileLayout.java
@@ -7,13 +7,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+
public class QuickTileLayout extends LinearLayout {
public QuickTileLayout(Context context) {
this(context, null);
}
- public QuickTileLayout(Context context, AttributeSet attrs) {
+ public QuickTileLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setGravity(Gravity.CENTER);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
index a9b2376e46e5..90118539c912 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
@@ -60,7 +60,9 @@ public class SlashDrawable extends Drawable {
private final RectF mSlashRect = new RectF(0, 0, 0, 0);
private float mRotation;
private boolean mSlashed;
+ @Nullable
private Mode mTintMode;
+ @Nullable
private ColorStateList mTintList;
private boolean mAnimationEnabled = true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index bff318a6f44e..da82d2cb7e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -9,6 +9,8 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -49,7 +51,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
this(context, null);
}
- public TileLayout(Context context, AttributeSet attrs) {
+ public TileLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
mLessRows = ((Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
@@ -67,7 +69,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
}
@Override
- public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+ public void setListening(boolean listening, @Nullable UiEventLogger uiEventLogger) {
if (mListening == listening) return;
mListening = listening;
for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index ca8f68160454..bc62416ca3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -20,6 +20,8 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.List;
@@ -37,12 +39,19 @@ public class TouchAnimator {
private final float mStartDelay;
private final float mEndDelay;
private final float mSpan;
+ @Nullable
private final Interpolator mInterpolator;
+ @Nullable
private final Listener mListener;
private float mLastT = -1;
- private TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets,
- float startDelay, float endDelay, Interpolator interpolator, Listener listener) {
+ private TouchAnimator(
+ Object[] targets,
+ KeyframeSet[] keyframeSets,
+ float startDelay,
+ float endDelay,
+ @Nullable Interpolator interpolator,
+ @Nullable Listener listener) {
mTargets = targets;
mKeyframeSets = keyframeSets;
mStartDelay = startDelay;
@@ -126,7 +135,9 @@ public class TouchAnimator {
private float mStartDelay;
private float mEndDelay;
+ @Nullable
private Interpolator mInterpolator;
+ @Nullable
private Listener mListener;
public Builder addFloat(Object target, String property, float... values) {
@@ -183,7 +194,8 @@ public class TouchAnimator {
return this;
}
- public Builder setInterpolator(Interpolator intepolator) {
+ /** Sets interpolator. */
+ public Builder setInterpolator(@Nullable Interpolator intepolator) {
mInterpolator = intepolator;
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 32ac73375f1d..2959c3b30eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -25,6 +25,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
@@ -40,6 +41,7 @@ public class QSCarrier extends LinearLayout {
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
private View mSpacer;
+ @Nullable
private CellSignalState mLastSignalState;
private boolean mProviderModelInitialized = false;
private boolean mIsSingleCarrier;
@@ -125,7 +127,7 @@ public class QSCarrier extends LinearLayout {
return true;
}
- private boolean hasValidTypeContentDescription(String typeContentDescription) {
+ private boolean hasValidTypeContentDescription(@Nullable String typeContentDescription) {
return TextUtils.equals(typeContentDescription,
mContext.getString(R.string.data_connection_no_internet))
|| TextUtils.equals(typeContentDescription,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 9ebdb1cf35fe..b59c0ccc510a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -28,6 +28,7 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toolbar;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
@@ -107,7 +108,7 @@ public class QSCustomizer extends LinearLayout {
mQsContainerController = controller;
}
- public void setQs(QS qs) {
+ public void setQs(@Nullable QS qs) {
mQs = qs;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 618a429f6b51..97390112c3ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -28,6 +28,7 @@ import android.widget.TextView;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
+import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -198,7 +199,7 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
}
/** */
- public void setQs(QSFragment qsFragment) {
+ public void setQs(@Nullable QSFragment qsFragment) {
mView.setQs(qsFragment);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index eae256532c54..b29687fe74c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -31,6 +31,8 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.widget.Button;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -79,7 +81,7 @@ public class TileQueryHelper {
mUserTracker = userTracker;
}
- public void setListener(TileStateListener listener) {
+ public void setListener(@Nullable TileStateListener listener) {
mListener = listener;
}
@@ -269,6 +271,7 @@ public class TileQueryHelper {
});
}
+ @Nullable
private State getState(Collection<QSTile> tiles, String spec) {
for (QSTile tile : tiles) {
if (spec.equals(tile.getTileSpec())) {
@@ -278,7 +281,8 @@ public class TileQueryHelper {
return null;
}
- private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
+ private void addTile(
+ String spec, @Nullable CharSequence appLabel, State state, boolean isSystem) {
if (mSpecs.contains(spec)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 10efec309971..4f15351322ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -89,7 +89,9 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private final TileServiceManager mServiceManager;
private final int mUser;
private final CustomTileStatePersister mCustomTileStatePersister;
+ @Nullable
private android.graphics.drawable.Icon mDefaultIcon;
+ @Nullable
private CharSequence mDefaultLabel;
private final Context mUserContext;
@@ -197,8 +199,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
/**
* Compare two icons, only works for resources.
*/
- private boolean iconEquals(android.graphics.drawable.Icon icon1,
- android.graphics.drawable.Icon icon2) {
+ private boolean iconEquals(@Nullable android.graphics.drawable.Icon icon1,
+ @Nullable android.graphics.drawable.Icon icon2) {
if (icon1 == icon2) {
return true;
}
@@ -372,6 +374,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
Uri.fromParts("package", mComponent.getPackageName(), null));
}
+ @Nullable
private Intent resolveIntent(Intent i) {
ResolveInfo result = mContext.getPackageManager().resolveActivityAsUser(i, 0, mUser);
return result != null ? new Intent(TileService.ACTION_QS_TILE_PREFERENCES)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index e6612fe8f08b..17ae7ffa9980 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -36,6 +36,7 @@ import android.service.quicksettings.TileService;
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -85,6 +86,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
private final BroadcastDispatcher mBroadcastDispatcher;
private Set<Integer> mQueuedMessages = new ArraySet<>();
+ @Nullable
private QSTileServiceWrapper mWrapper;
private boolean mListening;
private IBinder mClickBinder;
@@ -95,6 +97,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
private boolean mUnbindImmediate;
+ @Nullable
private TileChangeListener mChangeListener;
// Return value from bindServiceAsUser, determines whether safe to call unbind.
private boolean mIsBound;
@@ -466,6 +469,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
}
}
+ @Nullable
@Override
public IBinder asBinder() {
return mWrapper != null ? mWrapper.asBinder() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index bfa2aaa4e785..0a3c17c9045a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -35,6 +35,8 @@ import android.service.quicksettings.TileService;
import android.util.ArrayMap;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -297,6 +299,7 @@ public class TileServices extends IQSService.Stub {
}
}
+ @Nullable
@Override
public Tile getTile(IBinder token) {
CustomTile customTile = getTileForToken(token);
@@ -330,12 +333,14 @@ public class TileServices extends IQSService.Stub {
return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing();
}
+ @Nullable
private CustomTile getTileForToken(IBinder token) {
synchronized (mServices) {
return mTokenMap.get(token);
}
}
+ @Nullable
private CustomTile getTileForComponent(ComponentName component) {
synchronized (mServices) {
return mTiles.get(component);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 76950d1448d6..5e68f611293e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -18,6 +18,8 @@ import android.content.Context;
import android.os.Build;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSIconView;
@@ -173,6 +175,8 @@ public class QSFactoryImpl implements QSFactory {
mColorCorrectionTileProvider = colorCorrectionTileProvider;
}
+ /** Creates a tile with a type based on {@code tileSpec} */
+ @Nullable
public final QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
@@ -182,6 +186,7 @@ public class QSFactoryImpl implements QSFactory {
return tile;
}
+ @Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index b5f07d175274..6d9d5b16fcab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -116,6 +116,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private boolean mAnnounceNextStateChange;
private String mTileSpec;
+ @Nullable
private EnforcedAdmin mEnforcedAdmin;
private boolean mShowingDetail;
private int mIsFullQs;
@@ -260,6 +261,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
return new QSIconViewImpl(context);
}
+ /** Returns corresponding DetailAdapter. */
+ @Nullable
public DetailAdapter getDetailAdapter() {
return null; // optional
}
@@ -342,7 +345,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
refreshState(null);
}
- protected final void refreshState(Object arg) {
+ protected final void refreshState(@Nullable Object arg) {
mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();
}
@@ -432,9 +435,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*
* @return the intent to launch
*/
+ @Nullable
public abstract Intent getLongClickIntent();
- protected void handleRefreshState(Object arg) {
+ protected void handleRefreshState(@Nullable Object arg) {
handleUpdateState(mTmpState, arg);
boolean changed = mTmpState.copyTo(mState);
if (mReadyState == READY_STATE_READYING) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
index 72c68ce12ca4..f1e82b6a18a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -27,6 +27,7 @@ import com.android.systemui.qs.SlashDrawable;
public class SlashImageView extends ImageView {
+ @Nullable
@VisibleForTesting
protected SlashDrawable mSlash;
private boolean mAnimationEnabled = true;
@@ -35,6 +36,7 @@ public class SlashImageView extends ImageView {
super(context);
}
+ @Nullable
protected SlashDrawable getSlash() {
return mSlash;
}
@@ -52,7 +54,7 @@ public class SlashImageView extends ImageView {
}
@Override
- public void setImageDrawable(Drawable drawable) {
+ public void setImageDrawable(@Nullable Drawable drawable) {
if (drawable == null) {
mSlash = null;
super.setImageDrawable(null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 5552105a77c0..754f8e26ee87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -320,6 +320,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
// We probably won't ever have space in the UI for more than 20 devices, so don't
// get info for them.
private static final int MAX_DEVICES = 20;
+ @Nullable
private QSDetailItems mItems;
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index e5601f29af0b..698a2538ee87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -257,7 +257,9 @@ public class CellularTile extends QSTileImpl<SignalState> {
private static final class CallbackInfo {
boolean airplaneModeEnabled;
+ @Nullable
CharSequence dataSubscriptionName;
+ @Nullable
CharSequence dataContentDescription;
boolean activityIn;
boolean activityOut;
@@ -320,6 +322,7 @@ public class CellularTile extends QSTileImpl<SignalState> {
return mContext.getString(R.string.quick_settings_cellular_detail_title);
}
+ @Nullable
@Override
public Boolean getToggleState() {
return mDataController.isMobileDataSupported()
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 49c548da63f5..a06dc8b2c19a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -360,6 +360,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
+ @Nullable
private ZenModePanel mZenPanel;
private boolean mAuto;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cd81b4a11703..9df942d3260a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -145,13 +145,15 @@ public class InternetTile extends QSTileImpl<SignalState> {
&& mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
}
- private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+ @Nullable
+ private CharSequence getSecondaryLabel(boolean isTransient, @Nullable String statusLabel) {
return isTransient
? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
: statusLabel;
}
- private static String removeDoubleQuotes(String string) {
+ @Nullable
+ private static String removeDoubleQuotes(@Nullable String string) {
if (string == null) return null;
final int length = string.length();
if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
@@ -163,6 +165,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
private static final class EthernetCallbackInfo {
boolean mConnected;
int mEthernetSignalIconId;
+ @Nullable
String mEthernetContentDescription;
@Override
@@ -180,11 +183,14 @@ public class InternetTile extends QSTileImpl<SignalState> {
boolean mEnabled;
boolean mConnected;
int mWifiSignalIconId;
+ @Nullable
String mSsid;
boolean mActivityIn;
boolean mActivityOut;
+ @Nullable
String mWifiSignalContentDescription;
boolean mIsTransient;
+ @Nullable
public String mStatusLabel;
boolean mNoDefaultNetwork;
boolean mNoValidatedNetwork;
@@ -211,7 +217,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
private static final class CellularCallbackInfo {
boolean mAirplaneModeEnabled;
+ @Nullable
CharSequence mDataSubscriptionName;
+ @Nullable
CharSequence mDataContentDescription;
int mMobileSignalIconId;
int mQsTypeIcon;
@@ -540,7 +548,8 @@ public class InternetTile extends QSTileImpl<SignalState> {
}
}
- private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+ private CharSequence appendMobileDataType(
+ @Nullable CharSequence current, @Nullable CharSequence dataType) {
if (TextUtils.isEmpty(dataType)) {
return Html.fromHtml((current == null ? "" : current.toString()), 0);
}
@@ -551,6 +560,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
return Html.fromHtml(concat, 0);
}
+ @Nullable
private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
String roaming = mContext.getString(R.string.data_connection_roaming);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 0886b46f9d2f..a61f0ce0c864 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -54,6 +54,7 @@ public class NfcTile extends QSTileImpl<BooleanState> {
private static final String NFC = "nfc";
private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
+ @Nullable
private NfcAdapter mAdapter;
private BroadcastDispatcher mBroadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 9996ecd74fd2..b65802506cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -131,6 +131,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
return mQRCodeScannerController.isCameraAvailable();
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index d9919bdac889..247f02b120db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -72,8 +72,10 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
private final SecureSettings mSecureSettings;
private final QuickAccessWalletController mController;
+ @Nullable
private WalletCard mSelectedCard;
private boolean mIsWalletUpdating = true;
+ @Nullable
@VisibleForTesting Drawable mCardViewDrawable;
@Inject
@@ -200,6 +202,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
&& mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null;
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 8ff75cb3662d..45e43ee20d67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -136,6 +136,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
return 0;
}
+ @Nullable
@Override
public Intent getLongClickIntent() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index b0a1b18a8cd5..0be0619145b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -134,6 +134,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS
return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
}
+ @Nullable
@Override
public DetailAdapter getDetailAdapter() {
return super.getDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 6ca550c9ddb2..076ef3573dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -28,6 +28,8 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.internal.util.ArrayUtils;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -50,15 +52,15 @@ public class UserDetailItemView extends LinearLayout {
this(context, null);
}
- public UserDetailItemView(Context context, AttributeSet attrs) {
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
- public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
+ public UserDetailItemView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index f793a5086871..ce6aaae6e98c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -77,6 +77,7 @@ public class UserDetailView extends PseudoGridView {
private final Context mContext;
protected UserSwitcherController mController;
+ @Nullable
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index e110a6492b20..db1b6e68640e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -48,6 +48,7 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On
private final UserSwitcherController mUserSwitcherController;
private final UserInfoController mUserInfoController;
+ @Nullable
private Pair<String, Drawable> mLastUpdate;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 1608248f7c54..c82ff341bb12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -258,6 +258,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
}
+ @Nullable
private static String removeDoubleQuotes(String string) {
if (string == null) return null;
final int length = string.length();
@@ -271,11 +272,14 @@ public class WifiTile extends QSTileImpl<SignalState> {
boolean enabled;
boolean connected;
int wifiSignalIconId;
+ @Nullable
String ssid;
boolean activityIn;
boolean activityOut;
+ @Nullable
String wifiSignalContentDescription;
boolean isTransient;
+ @Nullable
public String statusLabel;
@Override
@@ -321,7 +325,9 @@ public class WifiTile extends QSTileImpl<SignalState> {
protected class WifiDetailAdapter implements DetailAdapter,
AccessPointController.AccessPointCallback, QSDetailItems.Callback {
+ @Nullable
private QSDetailItems mItems;
+ @Nullable
private WifiEntry[] mAccessPoints;
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 544246ee97aa..4fe155cfaeb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -52,6 +52,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
private final InternetDialogController mInternetDialogController;
+ @Nullable
private List<WifiEntry> mWifiEntries;
@VisibleForTesting
protected int mWifiEntriesCount;
@@ -189,6 +190,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
mWifiSummaryText.setText(summary);
}
+ @Nullable
Drawable getWifiDrawable(int level, boolean hasNoInternet) {
// If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
// will be returned.
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 e7982bfb7bcd..8e019426af14 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
@@ -42,7 +42,6 @@ import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
@@ -98,6 +97,7 @@ public class InternetDialog extends SystemUIDialog implements
private InternetDialogFactory mInternetDialogFactory;
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
+ @Nullable
private AlertDialog mAlertDialog;
private UiEventLogger mUiEventLogger;
private Context mContext;
@@ -130,12 +130,14 @@ public class InternetDialog extends SystemUIDialog implements
private Button mDoneButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
+ @Nullable
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
// Wi-Fi entries
private int mWifiNetworkHeight;
+ @Nullable
@VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
@VisibleForTesting
@@ -536,6 +538,7 @@ public class InternetDialog extends SystemUIDialog implements
return mInternetDialogController.getDialogTitleText();
}
+ @Nullable
CharSequence getSubtitleText() {
return mInternetDialogController.getSubtitleText(
mIsProgressBarVisible && !mIsSearchingHidden);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 3189d2f95c28..f89b7a3c0971 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -303,6 +303,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ @Nullable
protected Intent getWifiDetailsSettingsIntent(String key) {
if (TextUtils.isEmpty(key)) {
if (DEBUG) {
@@ -320,6 +321,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return mContext.getText(R.string.quick_settings_internet_label);
}
+ @Nullable
CharSequence getSubtitleText(boolean isProgressBarVisible) {
if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
// When Wi-Fi is disabled.
@@ -391,6 +393,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return null;
}
+ @Nullable
Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f380911b6403..84e21e4126cc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -113,7 +113,8 @@ public class TakeScreenshotService extends Service {
@Override
public IBinder onBind(@NonNull Intent intent) {
- registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
+ registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS),
+ Context.RECEIVER_EXPORTED);
final Messenger m = new Messenger(mHandler);
if (DEBUG_SERVICE) {
Log.d(TAG, "onBind: returning connection: " + m);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 3cecbb71407a..597e4242af66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -348,11 +348,19 @@ public class CommandQueue extends IStatusBar.Stub implements
String packageName) { }
/**
- * @see IStatusBar#showTransient(int, int[]).
+ * @see IStatusBar#showTransient(int, int[], boolean).
*/
default void showTransient(int displayId, @InternalInsetsType int[] types) { }
/**
+ * @see IStatusBar#showTransient(int, int[], boolean).
+ */
+ default void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
+ showTransient(displayId, types);
+ }
+
+ /**
* @see IStatusBar#abortTransient(int, int[]).
*/
default void abortTransient(int displayId, @InternalInsetsType int[] types) { }
@@ -1038,9 +1046,10 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
- public void showTransient(int displayId, int[] types) {
+ public void showTransient(int displayId, int[] types, boolean isGestureOnSystemBar) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget();
+ mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, isGestureOnSystemBar ? 1 : 0,
+ types).sendToTarget();
}
}
@@ -1444,8 +1453,9 @@ public class CommandQueue extends IStatusBar.Stub implements
case MSG_SHOW_TRANSIENT: {
final int displayId = msg.arg1;
final int[] types = (int[]) msg.obj;
+ final boolean isGestureOnSystemBar = msg.arg2 != 0;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showTransient(displayId, types);
+ mCallbacks.get(i).showTransient(displayId, types, isGestureOnSystemBar);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
index 4272bb14ff3a..66591b3c7d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -74,9 +74,12 @@ class DisableFlagsLogger constructor(
* Returns a string representing the, old, new, and new-after-modification disable flag states,
* as well as the differences between each of the states.
*
- * Example:
- * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
- * EnaihBcRso.qInGR (.n)
+ * Example if [old], [new], and [newAfterLocalModification] are all different:
+ * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (changed: hB.iGR) | New after local
+ * modification: EnaihBcRso.qInGR (changed: .n)
+ *
+ * Example if [old] and [new] are the same:
+ * EnaihBcRso.qiNGR (unchanged)
*
* A capital character signifies the flag is set and a lowercase character signifies that the
* flag isn't set. The flag states will be logged in the same order as the passed-in lists.
@@ -96,54 +99,51 @@ class DisableFlagsLogger constructor(
new: DisableState,
newAfterLocalModification: DisableState? = null
): String {
- val builder = StringBuilder("Received new disable state. ")
+ val builder = StringBuilder("Received new disable state: ")
- old?.let {
+ // This if/else has slightly repetitive code but is easier to read.
+ if (old != null && old != new) {
builder.append("Old: ")
builder.append(getFlagsString(old))
builder.append(" | ")
- }
-
- builder.append("New: ")
- if (old != null && old != new) {
- builder.append(getFlagsStringWithDiff(old, new))
- } else {
+ builder.append("New: ")
+ builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ } else if (old != null && old == new) {
+ // If old and new are the same, we only need to print one of them.
builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ } else { // old == null
+ builder.append(getFlagsString(new))
+ // Don't get a diff string because we have no [old] to compare with.
}
if (newAfterLocalModification != null && new != newAfterLocalModification) {
builder.append(" | New after local modification: ")
- builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
+ builder.append(getFlagsString(newAfterLocalModification))
+ builder.append(" ")
+ builder.append(getDiffString(new, newAfterLocalModification))
}
return builder.toString()
}
/**
- * Returns a string representing [new] state, as well as the difference from [old] to [new]
- * (if there is one).
- */
- private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
- val builder = StringBuilder()
- builder.append(getFlagsString(new))
- builder.append(" ")
- builder.append(getDiffString(old, new))
- return builder.toString()
- }
-
- /**
- * Returns a string representing the difference between [old] and [new], or an empty string if
- * there is no difference.
+ * Returns a string representing the difference between [old] and [new].
*
- * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
- * "(BC.e)".
+ * - If [old] was "abc.DE" and [new] was "aBC.De", the difference returned would be
+ * "(changed: BC.e)".
+ * - If [old] and [new] are the same, the difference returned would be "(unchanged)".
*/
private fun getDiffString(old: DisableState, new: DisableState): String {
if (old == new) {
- return ""
+ return "(unchanged)"
}
val builder = StringBuilder("(")
+ builder.append("changed: ")
disable1FlagsList.forEach {
val newSymbol = it.getFlagStatus(new.disable1)
if (it.getFlagStatus(old.disable1) != newSymbol) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8ce73d777087..cd4b74514937 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -133,7 +133,7 @@ public class KeyguardIndicationController {
private final DockManager mDockManager;
private final DevicePolicyManager mDevicePolicyManager;
private final UserManager mUserManager;
- private final @Main DelayableExecutor mExecutor;
+ protected final @Main DelayableExecutor mExecutor;
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private final FalsingManager mFalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 491a1750a93e..648e14cddff6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -8,12 +8,13 @@ import android.content.Context
import android.content.res.Configuration
import android.os.SystemClock
import android.util.DisplayMetrics
+import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
@@ -22,23 +23,26 @@ import com.android.systemui.biometrics.UdfpsKeyguardViewController
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L
@@ -51,19 +55,19 @@ private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f
@SysUISingleton
class LockscreenShadeTransitionController @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController,
- private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val logger: LSShadeTransitionLogger,
private val keyguardBypassController: KeyguardBypassController,
private val lockScreenUserManager: NotificationLockscreenUserManager,
private val falsingCollector: FalsingCollector,
private val ambientState: AmbientState,
- private val displayMetrics: DisplayMetrics,
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimController: ScrimController,
private val depthController: NotificationShadeDepthController,
private val context: Context,
configurationController: ConfigurationController,
- falsingManager: FalsingManager
-) {
+ falsingManager: FalsingManager,
+ dumpManager: DumpManager,
+) : Dumpable {
private var pulseHeight: Float = 0f
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
@@ -139,6 +143,23 @@ class LockscreenShadeTransitionController @Inject constructor(
touchHelper.updateResources(context)
}
})
+ dumpManager.registerDumpable(this)
+ statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
+ override fun onExpandedChanged(isExpanded: Boolean) {
+ // safeguard: When the panel is fully collapsed, let's make sure to reset.
+ // See b/198098523
+ if (!isExpanded) {
+ if (dragDownAmount != 0f && dragDownAnimator?.isRunning != true) {
+ logger.logDragDownAmountResetWhenFullyCollapsed()
+ dragDownAmount = 0f
+ }
+ if (pulseHeight != 0f && pulseHeightAnimator?.isRunning != true) {
+ logger.logPulseHeightNotResetWhenFullyCollapsed()
+ setPulseHeight(0f, animate = false)
+ }
+ }
+ }
+ })
}
private fun updateResources() {
@@ -182,19 +203,19 @@ class LockscreenShadeTransitionController @Inject constructor(
*/
internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) {
if (canDragDown()) {
+ val cancelRunnable = Runnable {
+ logger.logGoingToLockedShadeAborted()
+ setDragDownAmountAnimated(0f)
+ }
if (nsslController.isInLockedDownShade()) {
+ logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
statusbar.dismissKeyguardThenExecute(OnDismissAction {
nextHideKeyguardNeedsNoAnimation = true
false
- },
- null /* cancelRunnable */, false /* afterKeyguardGone */)
+ }, cancelRunnable, false /* afterKeyguardGone */)
} else {
- lockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (dragLengthY / displayMetrics.density).toInt(),
- 0 /* velocityDp */)
- lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ logger.logDraggedDown(startingChild, dragLengthY)
if (!ambientState.isDozing() || startingChild != null) {
// go to locked shade while animating the drag down amount from its current
// value
@@ -216,11 +237,11 @@ class LockscreenShadeTransitionController @Inject constructor(
dragDownAmount = 0f
forceApplyAmount = false
}
- val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) }
goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable)
}
}
} else {
+ logger.logUnSuccessfulDragDown(startingChild)
setDragDownAmountAnimated(0f)
}
}
@@ -229,6 +250,7 @@ class LockscreenShadeTransitionController @Inject constructor(
* Called by the touch helper when the drag down was aborted and should be reset.
*/
internal fun onDragDownReset() {
+ logger.logDragDownAborted()
nsslController.setDimmed(true /* dimmed */, true /* animated */)
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
@@ -246,10 +268,16 @@ class LockscreenShadeTransitionController @Inject constructor(
/**
* Called by the touch helper when the drag down was started
*/
- internal fun onDragDownStarted() {
+ internal fun onDragDownStarted(startingChild: ExpandableView?) {
+ logger.logDragDownStarted(startingChild)
nsslController.cancelLongPress()
nsslController.checkSnoozeLeavebehind()
- dragDownAnimator?.cancel()
+ dragDownAnimator?.apply {
+ if (isRunning) {
+ logger.logAnimationCancelled(isPulse = false)
+ cancel()
+ }
+ }
}
/**
@@ -294,12 +322,12 @@ class LockscreenShadeTransitionController @Inject constructor(
set(value) {
if (field != value || forceApplyAmount) {
field = value
- if (!nsslController.isInLockedDownShade() || forceApplyAmount) {
+ if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
nsslController.setTransitionToFullShadeAmount(field)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- dragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
- qS.setTransitionToFullShadeAmount(field, dragProgress)
+ qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ qS.setTransitionToFullShadeAmount(field, qSDragProgress)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
@@ -308,7 +336,10 @@ class LockscreenShadeTransitionController @Inject constructor(
}
}
- var dragProgress = 0f
+ /**
+ * The drag progress of the quick settings drag down amount
+ */
+ var qSDragProgress = 0f
private set
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
@@ -325,6 +356,7 @@ class LockscreenShadeTransitionController @Inject constructor(
delay: Long = 0,
endlistener: (() -> Unit)? = null
) {
+ logger.logDragDownAnimation(target)
val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN
dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS
@@ -380,7 +412,9 @@ class LockscreenShadeTransitionController @Inject constructor(
*/
@JvmOverloads
fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) {
- if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ val isKeyguard = statusBarStateController.state == StatusBarState.KEYGUARD
+ logger.logTryGoToLockedShade(isKeyguard)
+ if (isKeyguard) {
val animationHandler: ((Long) -> Unit)?
if (needsQSAnimation) {
// Let's use the default animation
@@ -416,6 +450,7 @@ class LockscreenShadeTransitionController @Inject constructor(
) {
if (statusbar.isShadeDisabled) {
cancelAction?.run()
+ logger.logShadeDisabledOnGoToLockedShade()
return
}
var userId: Int = lockScreenUserManager.getCurrentUserId()
@@ -454,9 +489,11 @@ class LockscreenShadeTransitionController @Inject constructor(
}
cancelAction?.run()
}
+ logger.logShowBouncerOnGoToLockedShade()
statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
draggedDownEntry = entry
} else {
+ logger.logGoingToLockedShade(animationHandler != null)
statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
@@ -476,6 +513,7 @@ class LockscreenShadeTransitionController @Inject constructor(
* @param previousState which state were we in when we hid the keyguard?
*/
fun onHideKeyguard(delay: Long, previousState: Int) {
+ logger.logOnHideKeyguard()
if (animationHandlerOnKeyguardDismiss != null) {
animationHandlerOnKeyguardDismiss!!.invoke(delay)
animationHandlerOnKeyguardDismiss = null
@@ -498,6 +536,7 @@ class LockscreenShadeTransitionController @Inject constructor(
* not triggered by gestures, e.g. when clicking on the shelf or expand button.
*/
private fun performDefaultGoToFullShadeAnimation(delay: Long) {
+ logger.logDefaultGoToFullShadeAnimation(delay)
notificationPanelController.animateToFullShade(delay)
animateAppear(delay)
}
@@ -534,6 +573,7 @@ class LockscreenShadeTransitionController @Inject constructor(
* @param cancelled was the interaction cancelled and this is a reset?
*/
fun finishPulseAnimation(cancelled: Boolean) {
+ logger.logPulseExpansionFinished(cancelled)
if (cancelled) {
setPulseHeight(0f, animate = true)
} else {
@@ -546,7 +586,28 @@ class LockscreenShadeTransitionController @Inject constructor(
* Notify this class that a pulse expansion is starting
*/
fun onPulseExpansionStarted() {
- pulseHeightAnimator?.cancel()
+ logger.logPulseExpansionStarted()
+ pulseHeightAnimator?.apply {
+ if (isRunning) {
+ logger.logAnimationCancelled(isPulse = true)
+ cancel()
+ }
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ IndentingPrintWriter(pw, " ").let {
+ it.println("LSShadeTransitionController:")
+ it.increaseIndent()
+ it.println("pulseHeight: $pulseHeight")
+ it.println("useSplitShade: $useSplitShade")
+ it.println("dragDownAmount: $dragDownAmount")
+ it.println("qSDragProgress: $qSDragProgress")
+ it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
+ it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
+ it.println("hasPendingHandlerOnKeyguardDismiss: " +
+ "${animationHandlerOnKeyguardDismiss != null}")
+ }
}
}
@@ -626,7 +687,7 @@ class DragDownHelper(
captureStartingChild(initialTouchX, initialTouchY)
initialTouchY = y
initialTouchX = x
- dragDownCallback.onDragDownStarted()
+ dragDownCallback.onDragDownStarted(startingChild)
dragDownAmountOnStart = dragDownCallback.dragDownAmount
return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 761a20326200..ea51bd89df42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -24,15 +24,18 @@ import android.content.res.Configuration
import android.os.PowerManager
import android.os.PowerManager.WAKE_REASON_GESTURE
import android.os.SystemClock
+import android.util.IndentingPrintWriter
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
+import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
@@ -43,6 +46,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
@@ -61,8 +66,9 @@ constructor(
private val statusBarStateController: StatusBarStateController,
private val falsingManager: FalsingManager,
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
- private val falsingCollector: FalsingCollector
-) : Gefingerpoken {
+ private val falsingCollector: FalsingCollector,
+ dumpManager: DumpManager
+) : Gefingerpoken, Dumpable {
companion object {
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
@@ -120,6 +126,7 @@ constructor(
}
})
mPowerManager = context.getSystemService(PowerManager::class.java)
+ dumpManager.registerDumpable(this)
}
private fun initResources(context: Context) {
@@ -329,4 +336,17 @@ constructor(
fun onStartedWakingUp() {
isWakingToShadeLocked = false
}
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ IndentingPrintWriter(pw, " ").let {
+ it.println("PulseExpansionHandler:")
+ it.increaseIndent()
+ it.println("isExpanding: $isExpanding")
+ it.println("leavingLockscreen: $leavingLockscreen")
+ it.println("mPulsing: $mPulsing")
+ it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
+ it.println("qsExpanded: $qsExpanded")
+ it.println("bouncerShowing: $bouncerShowing")
+ }
+ }
}
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 4717b3afb66d..09c608df6ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -626,7 +626,6 @@ public class NotificationEntryManager implements
entry = new NotificationEntry(
notification,
ranking,
- mFgsFeatureController.isForegroundServiceDismissalEnabled(),
SystemClock.uptimeMillis());
mAllNotifications.add(entry);
mLeakDetector.trackInstance(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index cd8897ea229d..bd9383de3bab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -21,6 +21,7 @@ import android.provider.DeviceConfig
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
@@ -37,6 +38,7 @@ private var sUsePeopleFiltering: Boolean? = null
/**
* Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
*/
+@SysUISingleton
class NotificationSectionsFeatureManager @Inject constructor(
val proxy: DeviceConfigProxy,
val context: Context
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b6b9c3f1cf9d..bf81ea5c264c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -56,10 +56,12 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Pair;
+import android.util.Slog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -240,6 +242,10 @@ public class NotifCollection implements Dumpable {
Assert.isMainThread();
checkForReentrantCall();
+ // TODO (b/206842750): This method is called from (silent) clear all and non-clear all
+ // contexts and should be checking the NO_CLEAR flag, rather than depending on NSSL
+ // to pass in a properly filtered list of notifications
+
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
for (int i = 0; i < entriesToDismiss.size(); i++) {
NotificationEntry entry = entriesToDismiss.get(i).first;
@@ -742,12 +748,13 @@ public class NotifCollection implements Dumpable {
*
* See NotificationManager.cancelGroupChildrenByListLocked() for corresponding code.
*/
- private static boolean shouldAutoDismissChildren(
+ @VisibleForTesting
+ static boolean shouldAutoDismissChildren(
NotificationEntry entry,
String dismissedGroupKey) {
return entry.getSbn().getGroupKey().equals(dismissedGroupKey)
&& !entry.getSbn().getNotification().isGroupSummary()
- && !hasFlag(entry, Notification.FLAG_FOREGROUND_SERVICE)
+ && !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
&& !hasFlag(entry, Notification.FLAG_BUBBLE)
&& entry.getDismissState() != DISMISSED;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index d56938ae7422..f22acb78f302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -174,7 +174,6 @@ public final class NotificationEntry extends ListEntry {
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
- private boolean mAllowFgsDismissal;
private int mBucket = BUCKET_ALERTING;
@Nullable private Long mPendingAnimationDuration;
private boolean mIsMarkedForUserTriggeredMovement;
@@ -192,14 +191,6 @@ public final class NotificationEntry extends ListEntry {
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking,
- long creationTime) {
- this(sbn, ranking, false, creationTime);
- }
-
- public NotificationEntry(
- @NonNull StatusBarNotification sbn,
- @NonNull Ranking ranking,
- boolean allowFgsDismissal,
long creationTime
) {
super(requireNonNull(requireNonNull(sbn).getKey()), creationTime);
@@ -209,8 +200,6 @@ public final class NotificationEntry extends ListEntry {
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
-
- mAllowFgsDismissal = allowFgsDismissal;
}
@Override
@@ -743,13 +732,11 @@ public final class NotificationEntry extends ListEntry {
/**
* @return Can the underlying notification be cleared? This can be different from whether the
* notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
*/
- // TOOD: This logic doesn't belong on NotificationEntry. It should be moved to the
- // ForegroundsServiceDismissalFeatureController or some other controller that can be added
- // as a dependency to any class that needs to answer this question.
+ // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+ // that can be added as a dependency to any class that needs to answer this question.
public boolean isClearable() {
- if (!isDismissable()) {
+ if (!mSbn.isClearable()) {
return false;
}
@@ -757,7 +744,7 @@ public final class NotificationEntry extends ListEntry {
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
- if (!child.isDismissable()) {
+ if (!child.getSbn().isClearable()) {
return false;
}
}
@@ -766,28 +753,25 @@ public final class NotificationEntry extends ListEntry {
}
/**
- * Notifications might have any combination of flags:
- * - FLAG_ONGOING_EVENT
- * - FLAG_NO_CLEAR
- * - FLAG_FOREGROUND_SERVICE
- *
- * We want to allow dismissal of notifications that represent foreground services, which may
- * have all 3 flags set. If we only find NO_CLEAR though, we don't want to allow dismissal
+ * @return Can the underlying notification be individually dismissed?
+ * @see #canViewBeDismissed()
*/
- private boolean isDismissable() {
- boolean ongoing = ((mSbn.getNotification().flags & Notification.FLAG_ONGOING_EVENT) != 0);
- boolean noclear = ((mSbn.getNotification().flags & Notification.FLAG_NO_CLEAR) != 0);
- boolean fgs = ((mSbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0);
-
- if (mAllowFgsDismissal) {
- if (noclear && !ongoing && !fgs) {
- return false;
+ // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
+ // that can be added as a dependency to any class that needs to answer this question.
+ public boolean isDismissable() {
+ if (mSbn.isOngoing()) {
+ return false;
+ }
+ List<NotificationEntry> children = getAttachedNotifChildren();
+ if (children != null && children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry child = children.get(i);
+ if (child.getSbn().isOngoing()) {
+ return false;
+ }
}
- return true;
- } else {
- return mSbn.isClearable();
}
-
+ return true;
}
public boolean canViewBeDismissed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index f50038c08bd3..3bd91b5c8480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -111,7 +111,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
String group = entry.getSbn().getGroup();
if (mNotifCollection.isOnlyChildInGroup(entry)) {
NotificationEntry summary = mNotifCollection.getGroupSummary(group);
- if (summary != null && summary.isClearable()) return summary;
+ if (summary != null && summary.isDismissable()) return summary;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index 3b114bbfd33a..8daf8be0cc8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -111,7 +111,7 @@ public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCal
public NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
- return groupSummary.isClearable() ? groupSummary : null;
+ return groupSummary.isDismissable() ? groupSummary : null;
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
new file mode 100644
index 000000000000..f949af0688be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
+import javax.inject.Inject
+
+@SysUISingleton
+class MediaContainerController @Inject constructor(
+ private val layoutInflater: LayoutInflater
+) : NodeController {
+
+ override val nodeLabel = "MediaContainer"
+ var mediaContainerView: MediaContainerView? = null
+ private set
+
+ fun reinflateView(parent: ViewGroup) {
+ var oldPos = -1
+ mediaContainerView?.let { _view ->
+ _view.removeFromTransientContainer()
+ if (_view.parent === parent) {
+ oldPos = parent.indexOfChild(_view)
+ parent.removeView(_view)
+ }
+ }
+ val inflated = layoutInflater.inflate(
+ R.layout.keyguard_media_container,
+ parent,
+ false /* attachToRoot */)
+ as MediaContainerView
+ if (oldPos != -1) {
+ parent.addView(inflated, oldPos)
+ }
+ mediaContainerView = inflated
+ }
+
+ override val view: View
+ get() = mediaContainerView!!
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index f59e4ab2007b..f13470ec2c94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,6 +33,8 @@ import com.android.systemui.util.traceSection
* need to present in the shade, notably the section headers.
*/
class NodeSpecBuilder(
+ private val mediaContainerController: MediaContainerController,
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val viewBarn: NotifViewBarn
) {
fun buildNodeSpec(
@@ -39,6 +42,13 @@ class NodeSpecBuilder(
notifList: List<ListEntry>
): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
val root = NodeSpecImpl(null, rootController)
+
+ // The media container should be added as the first child of the root node
+ // TODO: Perhaps the node spec building process should be more of a pipeline of its own?
+ if (sectionsFeatureManager.isMediaControlsEnabled()) {
+ root.children.add(NodeSpecImpl(root, mediaContainerController))
+ }
+
var currentSection: NotifSection? = null
val prevSections = mutableSetOf<NotifSection?>()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 8c15647c5038..4e9017e05ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -60,7 +60,7 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
override fun reinflateView(parent: ViewGroup) {
var oldPos = -1
_view?.let { _view ->
- _view.transientContainer?.removeView(_view)
+ _view.removeFromTransientContainer()
if (_view.parent === parent) {
oldPos = parent.indexOfChild(_view)
parent.removeView(_view)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 1a8d720a12c6..43a75a5d5da8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
import android.content.Context
import android.view.View
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -33,13 +34,15 @@ class ShadeViewManager constructor(
context: Context,
listContainer: NotificationListContainer,
private val stackController: NotifStackController,
+ mediaContainerController: MediaContainerController,
+ featureManager: NotificationSectionsFeatureManager,
logger: ShadeViewDifferLogger,
private val viewBarn: NotifViewBarn
) {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
- private val specBuilder = NodeSpecBuilder(viewBarn)
+ private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager, viewBarn)
private val viewDiffer = ShadeViewDiffer(rootController, logger)
/** Method for attaching this manager to the pipeline. */
@@ -68,6 +71,8 @@ class ShadeViewManager constructor(
class ShadeViewManagerFactory @Inject constructor(
private val context: Context,
private val logger: ShadeViewDifferLogger,
+ private val mediaContainerController: MediaContainerController,
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val viewBarn: NotifViewBarn
) {
fun create(listContainer: NotificationListContainer, stackController: NotifStackController) =
@@ -75,6 +80,8 @@ class ShadeViewManagerFactory @Inject constructor(
context,
listContainer,
stackController,
+ mediaContainerController,
+ sectionsFeatureManager,
logger,
viewBarn)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f8984704220e..08a230b18eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1462,7 +1462,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void performDismiss(boolean fromAccessibility) {
Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
dismiss(fromAccessibility);
- if (mEntry.isClearable()) {
+ if (mEntry.isDismissable()) {
if (mOnUserInteractionCallback != null) {
mOnUserInteractionCallback.onDismiss(mEntry, REASON_CANCEL,
mOnUserInteractionCallback.getGroupSummaryToDismiss(mEntry));
@@ -2673,9 +2673,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link NotificationEntry#isClearable()}.
+ * see {@link NotificationEntry#isDismissable()}.
*/
public boolean canViewBeDismissed() {
+ return mEntry.isDismissable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ }
+
+ /**
+ * @return Whether this view is allowed to be cleared with clear all. Only valid for visible
+ * notifications as otherwise some state might not be updated. To request about the general
+ * clearability see {@link NotificationEntry#isClearable()}.
+ */
+ public boolean canViewBeCleared() {
return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index e8e6e310322d..0b6d7594ce50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -32,7 +32,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
public class FooterView extends StackScrollerDecorView {
- private FooterViewButton mDismissButton;
+ private FooterViewButton mClearAllButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
@@ -57,16 +57,16 @@ public class FooterView extends StackScrollerDecorView {
pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
pw.println("manageButton showHistory: " + mShowHistory);
pw.println("manageButton visibility: "
- + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+ + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
pw.println("dismissButton visibility: "
- + DumpUtilsKt.visibilityString(mDismissButton.getVisibility()));
+ + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mDismissButton = (FooterViewButton) findSecondaryView();
+ mClearAllButton = (FooterViewButton) findSecondaryView();
mManageButton = findViewById(R.id.manage_text);
}
@@ -74,8 +74,8 @@ public class FooterView extends StackScrollerDecorView {
mManageButton.setOnClickListener(listener);
}
- public void setDismissButtonClickListener(OnClickListener listener) {
- mDismissButton.setOnClickListener(listener);
+ public void setClearAllButtonClickListener(OnClickListener listener) {
+ mClearAllButton.setOnClickListener(listener);
}
public boolean isOnEmptySpace(float touchX, float touchY) {
@@ -106,8 +106,8 @@ public class FooterView extends StackScrollerDecorView {
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateColors();
- mDismissButton.setText(R.string.clear_all_notifications_text);
- mDismissButton.setContentDescription(
+ mClearAllButton.setText(R.string.clear_all_notifications_text);
+ mClearAllButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
showHistory(mShowHistory);
}
@@ -118,8 +118,8 @@ public class FooterView extends StackScrollerDecorView {
public void updateColors() {
Resources.Theme theme = mContext.getTheme();
int textColor = getResources().getColor(R.color.notif_pill_text, theme);
- mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
- mDismissButton.setTextColor(textColor);
+ mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+ mClearAllButton.setTextColor(textColor);
mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
mManageButton.setTextColor(textColor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index e65846865ef8..7dc2e1949274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -57,7 +57,7 @@ public class AmbientState {
private int mTopPadding;
private boolean mShadeExpanded;
private float mMaxHeadsUpTranslation;
- private boolean mDismissAllInProgress;
+ private boolean mClearAllInProgress;
private int mLayoutMinHeight;
private int mLayoutMaxHeight;
private NotificationShelf mShelf;
@@ -384,12 +384,12 @@ public class AmbientState {
return mMaxHeadsUpTranslation;
}
- public void setDismissAllInProgress(boolean dismissAllInProgress) {
- mDismissAllInProgress = dismissAllInProgress;
+ public void setClearAllInProgress(boolean clearAllInProgress) {
+ mClearAllInProgress = clearAllInProgress;
}
- public boolean isDismissAllInProgress() {
- return mDismissAllInProgress;
+ public boolean isClearAllInProgress() {
+ return mClearAllInProgress;
}
public void setLayoutMinHeight(int layoutMinHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
index 0247a99bc6c0..c9a0f6c428c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java
@@ -25,9 +25,9 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
* Root view to insert Lock screen media controls into the notification stack.
*/
-public class MediaHeaderView extends ExpandableView {
+public class MediaContainerView extends ExpandableView {
- public MediaHeaderView(Context context, AttributeSet attrs) {
+ public MediaContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 464fd06587e9..b589d9ae1abf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -45,7 +45,7 @@ public class NotificationRoundnessManager {
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
private boolean mRoundForPulsingViews;
- private boolean mIsDismissAllInProgress;
+ private boolean mIsClearAllInProgress;
private ExpandableView mSwipedView = null;
private ExpandableView mViewBeforeSwipedView = null;
@@ -156,8 +156,8 @@ public class NotificationRoundnessManager {
}
}
- void setDismissAllInProgress(boolean isClearingAll) {
- mIsDismissAllInProgress = isClearingAll;
+ void setClearAllInProgress(boolean isClearingAll) {
+ mIsClearAllInProgress = isClearingAll;
}
private float getRoundnessFraction(ExpandableView view, boolean top) {
@@ -170,8 +170,8 @@ public class NotificationRoundnessManager {
return 1f;
}
if (view instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) view).canViewBeDismissed()
- && mIsDismissAllInProgress) {
+ && ((ExpandableNotificationRow) view).canViewBeCleared()
+ && mIsClearAllInProgress) {
return 1.0f;
}
if ((view.isPinned()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 1d90780ffee8..b02dc0cffdb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -16,17 +16,15 @@
package com.android.systemui.statusbar.notification.stack
import android.annotation.ColorInt
-import android.annotation.LayoutRes
import android.util.Log
-import android.view.LayoutInflater
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.R
import com.android.systemui.media.KeyguardMediaController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -60,6 +58,7 @@ class NotificationSectionsManager @Inject internal constructor(
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val logger: NotificationSectionsLogger,
private val notifPipelineFlags: NotifPipelineFlags,
+ private val mediaContainerController: MediaContainerController,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
@@ -68,7 +67,7 @@ class NotificationSectionsManager @Inject internal constructor(
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
- reinflateViews(LayoutInflater.from(parent.context))
+ reinflateViews()
}
}
@@ -91,39 +90,19 @@ class NotificationSectionsManager @Inject internal constructor(
val peopleHeaderView: SectionHeaderView?
get() = peopleHeaderController.headerView
- @get:VisibleForTesting
- var mediaControlsView: MediaHeaderView? = null
- private set
+ @VisibleForTesting
+ val mediaControlsView: MediaContainerView?
+ get() = mediaContainerController.mediaContainerView
/** Must be called before use. */
- fun initialize(parent: NotificationStackScrollLayout, layoutInflater: LayoutInflater) {
+ fun initialize(parent: NotificationStackScrollLayout) {
check(!initialized) { "NotificationSectionsManager already initialized" }
initialized = true
this.parent = parent
- reinflateViews(layoutInflater)
+ reinflateViews()
configurationController.addCallback(configurationListener)
}
- private fun <T : ExpandableView> reinflateView(
- view: T?,
- layoutInflater: LayoutInflater,
- @LayoutRes layoutResId: Int
- ): T {
- var oldPos = -1
- view?.let {
- view.transientContainer?.removeView(view)
- if (view.parent === parent) {
- oldPos = parent.indexOfChild(view)
- parent.removeView(view)
- }
- }
- val inflated = layoutInflater.inflate(layoutResId, parent, false) as T
- if (oldPos != -1) {
- parent.addView(inflated, oldPos)
- }
- return inflated
- }
-
fun createSectionsForBuckets(): Array<NotificationSection> =
sectionsFeatureManager.getNotificationBuckets()
.map { NotificationSection(parent, it) }
@@ -132,13 +111,12 @@ class NotificationSectionsManager @Inject internal constructor(
/**
* Reinflates the entire notification header, including all decoration views.
*/
- fun reinflateViews(layoutInflater: LayoutInflater) {
+ fun reinflateViews() {
silentHeaderController.reinflateView(parent)
alertingHeaderController.reinflateView(parent)
peopleHeaderController.reinflateView(parent)
incomingHeaderController.reinflateView(parent)
- mediaControlsView =
- reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
+ mediaContainerController.reinflateView(parent)
keyguardMediaController.attachSinglePaneContainer(mediaControlsView)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 943f05fe15ad..915a85df679c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -255,8 +255,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mIsCurrentUserSetup;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
- private boolean mDismissAllInProgress;
- private FooterDismissListener mFooterDismissListener;
+ private boolean mClearAllInProgress;
+ private FooterClearAllListener mFooterClearAllListener;
private boolean mFlingAfterUpEvent;
/**
@@ -439,8 +439,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private int mQsScrollBoundaryPosition;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
- private DismissListener mDismissListener;
- private DismissAllAnimationListener mDismissAllAnimationListener;
+ private ClearAllListener mClearAllListener;
+ private ClearAllAnimationListener mClearAllAnimationListener;
private ShadeController mShadeController;
private Consumer<Boolean> mOnStackYChanged;
@@ -574,7 +574,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
updateSplitNotificationShade();
- mSectionsManager.initialize(this, LayoutInflater.from(context));
+ mSectionsManager.initialize(this);
mSections = mSectionsManager.createSectionsForBuckets();
mAmbientState = Dependency.get(AmbientState.class);
@@ -665,7 +665,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
inflateFooterView();
inflateEmptyShadeView();
updateFooter();
- mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
+ mSectionsManager.reinflateViews();
}
public void setIsRemoteInputActive(boolean isActive) {
@@ -1207,7 +1207,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void clampScrollPosition() {
int scrollRange = getScrollRange();
- if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
+ if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
boolean animateStackY = false;
if (scrollRange < getScrollAmountToScrollBoundary()
&& mAnimateStackYForContentHeightChange) {
@@ -1729,7 +1729,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
- true /* isDismissAll */);
+ true /* isClearAll */);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2228,7 +2228,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
height += viewHeight;
numShownItems++;
- if (viewHeight > 0 || !(expandableView instanceof MediaHeaderView)) {
+ if (viewHeight > 0 || !(expandableView instanceof MediaContainerView)) {
// Only count the media as a notification if it has a positive height.
numShownNotifs++;
}
@@ -3202,7 +3202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ignoreChildren = false;
}
childWasSwipedOut |= isFullySwipedOut(row);
- } else if (child instanceof MediaHeaderView) {
+ } else if (child instanceof MediaContainerView) {
childWasSwipedOut = true;
}
if (!childWasSwipedOut) {
@@ -4071,8 +4071,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
clearTransient();
clearHeadsUpDisappearRunning();
- if (mAmbientState.isDismissAllInProgress()) {
- setDismissAllInProgress(false);
+ if (mAmbientState.isClearAllInProgress()) {
+ setClearAllInProgress(false);
if (mShadeNeedsToClose) {
mShadeNeedsToClose = false;
postDelayed(
@@ -4446,19 +4446,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setDismissAllInProgress(boolean dismissAllInProgress) {
- mDismissAllInProgress = dismissAllInProgress;
- mAmbientState.setDismissAllInProgress(dismissAllInProgress);
- mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
- handleDismissAllClipping();
+ public void setClearAllInProgress(boolean clearAllInProgress) {
+ mClearAllInProgress = clearAllInProgress;
+ mAmbientState.setClearAllInProgress(clearAllInProgress);
+ mController.getNoticationRoundessManager().setClearAllInProgress(clearAllInProgress);
+ handleClearAllClipping();
}
- boolean getDismissAllInProgress() {
- return mDismissAllInProgress;
+ boolean getClearAllInProgress() {
+ return mClearAllInProgress;
}
@ShadeViewRefactor(RefactorComponent.ADAPTER)
- private void handleDismissAllClipping() {
+ private void handleClearAllClipping() {
final int count = getChildCount();
boolean previousChildWillBeDismissed = false;
for (int i = 0; i < count; i++) {
@@ -4466,12 +4466,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (child.getVisibility() == GONE) {
continue;
}
- if (mDismissAllInProgress && previousChildWillBeDismissed) {
+ if (mClearAllInProgress && previousChildWillBeDismissed) {
child.setMinClipTopAmount(child.getClipTopAmount());
} else {
child.setMinClipTopAmount(0);
}
- previousChildWillBeDismissed = canChildBeDismissed(child);
+ previousChildWillBeDismissed = canChildBeCleared(child);
}
}
@@ -5022,7 +5022,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (isVisible(row) && includeChildInDismissAll(row, selection)) {
+ if (isVisible(row) && includeChildInClearAll(row, selection)) {
return true;
}
}
@@ -5052,7 +5052,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (isChildrenVisible(parent)) {
for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
- if (isVisible(child) && includeChildInDismissAll(child, selection)) {
+ if (isVisible(child) && includeChildInClearAll(child, selection)) {
viewsToHide.add(child);
}
}
@@ -5073,13 +5073,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
continue;
}
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- if (includeChildInDismissAll(parent, selection)) {
+ if (includeChildInClearAll(parent, selection)) {
viewsToRemove.add(parent);
}
List<ExpandableNotificationRow> children = parent.getAttachedChildren();
if (isVisible(parent) && children != null) {
for (ExpandableNotificationRow child : children) {
- if (includeChildInDismissAll(parent, selection)) {
+ if (includeChildInClearAll(parent, selection)) {
viewsToRemove.add(child);
}
}
@@ -5090,7 +5090,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Collects a list of visible rows, and animates them away in a staggered fashion as if they
- * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
+ * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@VisibleForTesting
@@ -5099,18 +5099,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
getRowsToDismissInBackend(selection);
- if (mDismissListener != null) {
- mDismissListener.onDismiss(selection);
+ if (mClearAllListener != null) {
+ mClearAllListener.onClearAll(selection);
}
final Runnable dismissInBackend = () -> {
- onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
+ onClearAllAnimationsEnd(rowsToDismissInBackend, selection);
};
if (viewsToAnimateAway.isEmpty()) {
dismissInBackend.run();
return;
}
// Disable normal animations
- setDismissAllInProgress(true);
+ setClearAllInProgress(true);
mShadeNeedsToClose = closeShade;
// Decrease the delay for every row we animate to give the sense of
@@ -5131,10 +5131,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- private boolean includeChildInDismissAll(
+ private boolean includeChildInClearAll(
ExpandableNotificationRow row,
@SelectedRows int selection) {
- return canChildBeDismissed(row) && matchesSelection(row, selection);
+ return canChildBeCleared(row) && matchesSelection(row, selection);
}
/** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
@@ -5150,9 +5150,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected void inflateFooterView() {
FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_footer, this, false);
- footerView.setDismissButtonClickListener(v -> {
- if (mFooterDismissListener != null) {
- mFooterDismissListener.onDismiss();
+ footerView.setClearAllButtonClickListener(v -> {
+ if (mFooterClearAllListener != null) {
+ mFooterClearAllListener.onClearAll();
}
clearNotifications(ROWS_ALL, true /* closeShade */);
footerView.setSecondaryVisible(false /* visible */, true /* animate */);
@@ -5389,20 +5389,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mCheckForLeavebehind;
}
- void setDismissListener (DismissListener listener) {
- mDismissListener = listener;
+ void setClearAllListener(ClearAllListener listener) {
+ mClearAllListener = listener;
}
- void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) {
- mDismissAllAnimationListener = dismissAllAnimationListener;
+ void setClearAllAnimationListener(ClearAllAnimationListener clearAllAnimationListener) {
+ mClearAllAnimationListener = clearAllAnimationListener;
}
public void setHighPriorityBeforeSpeedBump(boolean highPriorityBeforeSpeedBump) {
mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
}
- void setFooterDismissListener(FooterDismissListener listener) {
- mFooterDismissListener = listener;
+ void setFooterClearAllListener(FooterClearAllListener listener) {
+ mFooterClearAllListener = listener;
}
void setShadeController(ShadeController shadeController) {
@@ -5976,9 +5976,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
static boolean canChildBeDismissed(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (row.isBlockingHelperShowingAndTranslationFinished()) {
- return true;
- }
if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
return false;
}
@@ -5990,6 +5987,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return false;
}
+ static boolean canChildBeCleared(View v) {
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+ return false;
+ }
+ return row.canViewBeCleared();
+ }
+ if (v instanceof PeopleHubView) {
+ return ((PeopleHubView) v).getCanSwipe();
+ }
+ return false;
+ }
+
// --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
void onEntryUpdated(NotificationEntry entry) {
@@ -6003,11 +6014,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Called after the animations for a "clear all notifications" action has ended.
*/
- private void onDismissAllAnimationsEnd(
+ private void onClearAllAnimationsEnd(
List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) {
- if (mDismissAllAnimationListener != null) {
- mDismissAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
+ if (mClearAllAnimationListener != null) {
+ mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
}
}
@@ -6149,15 +6160,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
- interface DismissListener {
- void onDismiss(@SelectedRows int selectedRows);
+ interface ClearAllListener {
+ void onClearAll(@SelectedRows int selectedRows);
}
- interface FooterDismissListener {
- void onDismiss();
+ interface FooterClearAllListener {
+ void onClearAll();
}
- interface DismissAllAnimationListener {
+ interface ClearAllAnimationListener {
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index fc612a934b43..ff75eef80ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -28,7 +28,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
import android.content.res.Configuration;
@@ -466,7 +466,7 @@ public class NotificationStackScrollLayoutController {
*/
public void handleChildViewDismissed(View view) {
- if (mView.getDismissAllInProgress()) {
+ if (mView.getClearAllInProgress()) {
return;
}
mView.onSwipeEnd();
@@ -510,7 +510,7 @@ public class NotificationStackScrollLayoutController {
&& (parent.areGutsExposed()
|| mSwipeHelper.getExposedMenuView() == parent
|| (parent.getAttachedChildren().size() == 1
- && parent.getEntry().isClearable()))) {
+ && parent.getEntry().isDismissable()))) {
// In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the
@@ -720,10 +720,10 @@ public class NotificationStackScrollLayoutController {
mView.setController(this);
mView.setTouchHandler(new TouchHandler());
mView.setStatusBar(mStatusBar);
- mView.setDismissAllAnimationListener(this::onAnimationEnd);
- mView.setDismissListener((selection) -> mUiEventLogger.log(
+ mView.setClearAllAnimationListener(this::onAnimationEnd);
+ mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
- mView.setFooterDismissListener(() ->
+ mView.setFooterClearAllListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
@@ -1452,7 +1452,7 @@ public class NotificationStackScrollLayoutController {
}
} else {
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
- if (canChildBeDismissed(rowToRemove)) {
+ if (canChildBeCleared(rowToRemove)) {
mNotificationEntryManager.performRemoveNotification(
rowToRemove.getEntry().getSbn(),
getDismissedByUserStats(rowToRemove.getEntry()),
@@ -1503,7 +1503,7 @@ public class NotificationStackScrollLayoutController {
* from the keyguard host to the quick settings one.
*/
public int getFullShadeTransitionInset() {
- MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
if (view == null || view.getHeight() == 0
|| mStatusBarStateController.getState() != KEYGUARD) {
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2c70a5fd9ce7..8f0579cc4693 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -458,7 +458,7 @@ public class StackScrollAlgorithm {
final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
((FooterView.FooterViewState) viewState).hideContent =
isShelfShowing || noSpaceForFooter
- || (ambientState.isDismissAllInProgress()
+ || (ambientState.isClearAllInProgress()
&& !hasOngoingNotifs(algorithmState));
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
new file mode 100644
index 000000000000..4de78f5d6190
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.statusbar.phone
+
+import android.util.DisplayMetrics
+import android.view.View
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.LSShadeTransitionLog
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import javax.inject.Inject
+
+private const val TAG = "LockscreenShadeTransitionController"
+
+class LSShadeTransitionLogger @Inject constructor(
+ @LSShadeTransitionLog private val buffer: LogBuffer,
+ private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val displayMetrics: DisplayMetrics
+) {
+ fun logUnSuccessfulDragDown(startingChild: View?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Tried to drag down but can't drag down on $str1"
+ })
+ }
+
+ fun logDragDownAborted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "The drag down was reset"
+ })
+ }
+
+ fun logDragDownStarted(startingChild: ExpandableView?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "The drag down has started on $str1"
+ })
+ }
+
+ fun logDraggedDownLockDownShade(startingChild: View?) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Dragged down in locked down shade on $str1"
+ })
+ }
+
+ fun logDraggedDown(startingChild: View?, dragLengthY: Int) {
+ val entry = (startingChild as ExpandableNotificationRow?)?.entry
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = entry?.key ?: "no entry"
+ }, {
+ "Drag down succeeded on $str1"
+ })
+ // Do logging to event log not just our own buffer
+ lockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_SHADE,
+ (dragLengthY / displayMetrics.density).toInt(),
+ 0 /* velocityDp */)
+ lockscreenGestureLogger.log(
+ LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ }
+
+ fun logDefaultGoToFullShadeAnimation(delay: Long) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ long1 = delay
+ }, {
+ "Default animation started to full shade with delay $long1"
+ })
+ }
+
+ fun logTryGoToLockedShade(keyguard: Boolean) {
+ buffer.log(TAG, LogLevel.INFO, {
+ bool1 = keyguard
+ }, {
+ "Trying to go to locked shade " + if (bool1) "from keyguard" else "not from keyguard"
+ })
+ }
+
+ fun logShadeDisabledOnGoToLockedShade() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "The shade was disabled when trying to go to the locked shade"
+ })
+ }
+
+ fun logShowBouncerOnGoToLockedShade() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Showing bouncer when trying to go to the locked shade"
+ })
+ }
+
+ fun logGoingToLockedShade(customAnimationHandler: Boolean) {
+ buffer.log(TAG, LogLevel.INFO, {
+ bool1 = customAnimationHandler
+ }, {
+ "Going to locked shade " + if (customAnimationHandler) "with" else "without" +
+ " a custom handler"
+ })
+ }
+
+ fun logOnHideKeyguard() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Notified that the keyguard is being hidden"
+ })
+ }
+
+ fun logPulseExpansionStarted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion has started"
+ })
+ }
+
+ fun logPulseExpansionFinished(cancelled: Boolean) {
+ if (cancelled) {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion is requested to cancel"
+ })
+ } else {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Pulse Expansion is requested to finish"
+ })
+ }
+ }
+
+ fun logDragDownAnimation(target: Float) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ double1 = target.toDouble()
+ }, {
+ "Drag down amount animating to " + double1
+ })
+ }
+
+ fun logAnimationCancelled(isPulse: Boolean) {
+ if (isPulse) {
+ buffer.log(TAG, LogLevel.DEBUG, {}, {
+ "Pulse animation cancelled"
+ })
+ } else {
+ buffer.log(TAG, LogLevel.DEBUG, {}, {
+ "drag down animation cancelled"
+ })
+ }
+ }
+
+ fun logDragDownAmountResetWhenFullyCollapsed() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "Drag down amount stuck and reset after shade was fully collapsed"
+ })
+ }
+
+ fun logPulseHeightNotResetWhenFullyCollapsed() {
+ buffer.log(TAG, LogLevel.WARNING, {}, {
+ "Pulse height stuck and reset after shade was fully collapsed"
+ })
+ }
+
+ fun logGoingToLockedShadeAborted() {
+ buffer.log(TAG, LogLevel.INFO, {}, {
+ "Going to the Locked Shade has been aborted"
+ })
+ }
+}
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 03b1627b03c6..4d625cfbfbf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -183,7 +183,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
+import com.android.systemui.statusbar.notification.stack.MediaContainerView;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -1581,7 +1581,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (row.isRemoved()) {
continue;
}
- } else if (child instanceof MediaHeaderView) {
+ } else if (child instanceof MediaContainerView) {
if (child.getVisibility() == GONE) {
continue;
}
@@ -2433,7 +2433,7 @@ public class NotificationPanelViewController extends PanelViewController {
// mLockscreenShadeTransitionController.getDragProgress change.
// When in lockscreen, getDragProgress indicates the true expanded fraction of QS
float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
- ? mLockscreenShadeTransitionController.getDragProgress()
+ ? mLockscreenShadeTransitionController.getQSDragProgress()
: getExpandedFraction();
mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
@@ -4766,7 +4766,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public float getLockscreenShadeDragProgress() {
- return mLockscreenShadeTransitionController.getDragProgress();
+ return mLockscreenShadeTransitionController.getQSDragProgress();
}
};
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 51f2e6728b80..cf9a5dba0320 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -490,7 +490,8 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index e2d0bb9991ed..31407b1d5cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -66,6 +66,8 @@ public class ThemeOverlayApplier implements Dumpable {
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
+ static final String OVERLAY_CATEGORY_THEME_STYLE =
+ "android.theme.customization.theme_style";
static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source";
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c86d77bfcc9f..fb8055199d61 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -66,6 +66,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.monet.ColorScheme;
+import com.android.systemui.monet.Style;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -121,8 +122,8 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
private boolean mNeedsOverlayCreation;
// Dominant color extracted from wallpaper, NOT the color used on the overlay
protected int mMainWallpaperColor = Color.TRANSPARENT;
- // Accent color extracted from wallpaper, NOT the color used on the overlay
- protected int mWallpaperAccentColor = Color.TRANSPARENT;
+ // Theme variant: Vibrant, Tonal, Expressive, etc
+ private Style mThemeStyle = Style.TONAL_SPOT;
// Accent colors overlay
private FabricatedOverlay mSecondaryOverlay;
// Neutral system colors overlay
@@ -453,26 +454,20 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
private void reevaluateSystemTheme(boolean forceReload) {
final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId());
final int mainColor;
- final int accentCandidate;
if (currentColors == null) {
mainColor = Color.TRANSPARENT;
- accentCandidate = Color.TRANSPARENT;
} else {
mainColor = getNeutralColor(currentColors);
- accentCandidate = getAccentColor(currentColors);
}
- if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate
- && !forceReload) {
+ if (mMainWallpaperColor == mainColor && !forceReload) {
return;
}
-
mMainWallpaperColor = mainColor;
- mWallpaperAccentColor = accentCandidate;
if (mIsMonetEnabled) {
- mSecondaryOverlay = getOverlay(mWallpaperAccentColor, ACCENT);
- mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL);
+ mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+ mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
mNeedsOverlayCreation = true;
if (DEBUG) {
Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
@@ -497,11 +492,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
/**
* Given a color candidate, return an overlay definition.
*/
- protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
+ protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) {
boolean nightMode = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- mColorScheme = new ColorScheme(color, nightMode);
+ mColorScheme = new ColorScheme(color, nightMode, style);
List<Integer> colorShades = type == ACCENT
? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
String name = type == ACCENT ? "accent" : "neutral";
@@ -537,6 +532,7 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
currentUser);
if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson);
final Map<String, OverlayIdentifier> categoryToPackage = new ArrayMap<>();
+ Style newStyle = mThemeStyle;
if (!TextUtils.isEmpty(overlayPackageJson)) {
try {
JSONObject object = new JSONObject(overlayPackageJson);
@@ -547,11 +543,25 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
categoryToPackage.put(category, identifier);
}
}
+
+ try {
+ newStyle = Style.valueOf(
+ object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
+ } catch (IllegalArgumentException e) {
+ newStyle = Style.TONAL_SPOT;
+ }
} catch (JSONException e) {
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
}
}
+ if (mIsMonetEnabled && newStyle != mThemeStyle) {
+ mThemeStyle = newStyle;
+ mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
+ mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
+ mNeedsOverlayCreation = true;
+ }
+
// Let's generate system overlay if the style picker decided to override it.
OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE);
if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
@@ -561,9 +571,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
colorString = "#" + colorString;
}
int color = Color.parseColor(colorString);
- mNeutralOverlay = getOverlay(color, NEUTRAL);
+ mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle);
+ mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle);
mNeedsOverlayCreation = true;
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (Exception e) {
// Color.parseColor doesn't catch any exceptions from the calls it makes
Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e);
@@ -574,30 +586,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
// setting. We need to sanitize the input, otherwise the overlay transaction will
// fail.
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
- } catch (NumberFormatException e) {
- // This is a package name. All good, let's continue
- }
- }
-
- // Same for accent color.
- OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR);
- if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
- try {
- String colorString = accentPalette.getPackageName().toLowerCase();
- if (!colorString.startsWith("#")) {
- colorString = "#" + colorString;
- }
- int color = Color.parseColor(colorString);
- mSecondaryOverlay = getOverlay(color, ACCENT);
- mNeedsOverlayCreation = true;
- categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
- } catch (Exception e) {
- // Color.parseColor doesn't catch any exceptions from the calls it makes
- Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e);
- }
- } else if (!mIsMonetEnabled && accentPalette != null) {
- try {
- Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (NumberFormatException e) {
// This is a package name. All good, let's continue
@@ -642,7 +630,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mSystemColors=" + mCurrentColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
- pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
@@ -650,5 +637,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable {
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
+ pw.println("mThemeStyle=" + mThemeStyle);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
new file mode 100644
index 000000000000..1f5959e879bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProvider.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.unfold
+
+import android.annotation.IntDef
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+
+/** Reports device fold states for logging purposes. */
+// TODO(b/198305865): Log state changes.
+interface FoldStateLoggingProvider : CallbackController<FoldStateLoggingListener> {
+
+ fun init()
+ fun uninit()
+
+ interface FoldStateLoggingListener {
+ fun onFoldUpdate(foldStateUpdate: FoldStateChange)
+ }
+
+ @IntDef(prefix = ["LOGGED_FOLD_STATE_"], value = [FULLY_OPENED, FULLY_CLOSED, HALF_OPENED])
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class LoggedFoldedStates
+}
+
+data class FoldStateChange(
+ @LoggedFoldedStates val previous: Int,
+ @LoggedFoldedStates val current: Int,
+ val dtMillis: Long
+)
+
+const val FULLY_OPENED = 1
+const val FULLY_CLOSED = 2
+const val HALF_OPENED = 3
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
new file mode 100644
index 000000000000..2683971f852c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 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.unfold
+
+import android.util.Log
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.FoldStateLoggingProvider.LoggedFoldedStates
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.SystemClock
+
+/**
+ * Reports device fold states for logging purposes.
+ *
+ * Wraps the state provided by [FoldStateProvider] to output only [FULLY_OPENED], [FULLY_CLOSED] and
+ * [HALF_OPENED] for logging purposes.
+ *
+ * Note that [HALF_OPENED] state is only emitted after the device angle is stable for some timeout.
+ * Check [FoldStateProvider] impl for it.
+ *
+ * This doesn't log the following transitions:
+ * - [HALF_OPENED] -> [FULLY_OPENED]: not interesting, as there is no transition going on
+ * - [HALF_OPENED] -> [HALF_OPENED]: not meaningful.
+ */
+class FoldStateLoggingProviderImpl(
+ private val foldStateProvider: FoldStateProvider,
+ private val clock: SystemClock
+) : FoldStateLoggingProvider, FoldStateProvider.FoldUpdatesListener {
+
+ private val outputListeners: MutableList<FoldStateLoggingListener> = mutableListOf()
+
+ @LoggedFoldedStates private var lastState: Int? = null
+ private var actionStartMillis: Long? = null
+
+ override fun init() {
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
+
+ override fun uninit() {
+ foldStateProvider.removeCallback(this)
+ foldStateProvider.stop()
+ }
+
+ override fun onHingeAngleUpdate(angle: Float) {}
+
+ override fun onFoldUpdate(@FoldUpdate update: Int) {
+ val now = clock.elapsedRealtime()
+ when (update) {
+ FOLD_UPDATE_START_OPENING -> {
+ lastState = FULLY_CLOSED
+ actionStartMillis = now
+ }
+ FOLD_UPDATE_START_CLOSING -> actionStartMillis = now
+ FOLD_UPDATE_FINISH_HALF_OPEN -> dispatchState(HALF_OPENED)
+ FOLD_UPDATE_FINISH_FULL_OPEN -> dispatchState(FULLY_OPENED)
+ FOLD_UPDATE_FINISH_CLOSED -> dispatchState(FULLY_CLOSED)
+ }
+ }
+
+ private fun dispatchState(@LoggedFoldedStates current: Int) {
+ val now = clock.elapsedRealtime()
+ val previous = lastState
+ val lastActionStart = actionStartMillis
+
+ if (previous != null && previous != current && lastActionStart != null) {
+ val time = now - lastActionStart
+ val foldStateChange = FoldStateChange(previous, current, time)
+ outputListeners.forEach { it.onFoldUpdate(foldStateChange) }
+ if (DEBUG) {
+ Log.d(TAG, "From $previous to $current in $time")
+ }
+ }
+
+ actionStartMillis = null
+ lastState = current
+ }
+
+ override fun addCallback(listener: FoldStateLoggingListener) {
+ outputListeners.add(listener)
+ }
+
+ override fun removeCallback(listener: FoldStateLoggingListener) {
+ outputListeners.remove(listener)
+ }
+}
+
+private const val DEBUG = false
+private const val TAG = "FoldStateLoggingProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index ccde3162b177..07f9c5487c41 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -17,10 +17,11 @@
package com.android.systemui.unfold
import com.android.keyguard.KeyguardUnfoldTransition
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
-import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.kotlin.getOrNull
import dagger.BindsInstance
import dagger.Module
import dagger.Provides
@@ -36,15 +37,17 @@ annotation class SysUIUnfoldScope
/**
* Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
- * [@SysUIUnfoldScope]. Since [SysUIUnfoldComponent] depends upon:
+ * [@SysUIUnfoldScope].
+ *
+ * Since [SysUIUnfoldComponent] depends upon:
* * [Optional<UnfoldTransitionProgressProvider>]
* * [Optional<ScopedUnfoldTransitionProgressProvider>]
* * [Optional<NaturalRotationProgressProvider>]
+ *
* no objects will get constructed if these parameters are empty.
*/
@Module(subcomponents = [SysUIUnfoldComponent::class])
class SysUIUnfoldModule {
- constructor() {}
@Provides
@SysUISingleton
@@ -53,12 +56,16 @@ class SysUIUnfoldModule {
rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
@Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
factory: SysUIUnfoldComponent.Factory
- ) =
- provider.flatMap { p1 ->
- rotationProvider.flatMap { p2 ->
- scopedProvider.map { p3 -> factory.create(p1, p2, p3) }
- }
+ ): Optional<SysUIUnfoldComponent> {
+ val p1 = provider.getOrNull()
+ val p2 = rotationProvider.getOrNull()
+ val p3 = scopedProvider.getOrNull()
+ return if (p1 == null || p2 == null || p3 == null) {
+ Optional.empty()
+ } else {
+ Optional.of(factory.create(p1, p2, p3))
}
+ }
}
@SysUIUnfoldScope
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 75dfd48ad9f3..f2c156108ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -24,8 +24,10 @@ import android.view.IWindowManager
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.time.SystemClockImpl
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import dagger.Lazy
import dagger.Module
@@ -48,7 +50,7 @@ class UnfoldTransitionModule {
sensorManager: SensorManager,
@Main executor: Executor,
@Main handler: Handler
- ) =
+ ): Optional<UnfoldTransitionProgressProvider> =
if (config.isEnabled) {
Optional.of(
createUnfoldTransitionProgressProvider(
@@ -59,15 +61,47 @@ class UnfoldTransitionModule {
sensorManager,
handler,
executor,
- tracingTagPrefix = "systemui"
- )
- )
+ tracingTagPrefix = "systemui"))
} else {
Optional.empty()
}
@Provides
@Singleton
+ fun provideFoldStateProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: Lazy<LifecycleScreenStatusProvider>,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ @Main executor: Executor,
+ @Main handler: Handler
+ ): Optional<FoldStateProvider> =
+ if (!config.isHingeAngleEnabled) {
+ Optional.empty()
+ } else {
+ Optional.of(
+ createFoldStateProvider(
+ context,
+ config,
+ screenStatusProvider.get(),
+ deviceStateManager,
+ sensorManager,
+ handler,
+ executor))
+ }
+
+ @Provides
+ @Singleton
+ fun providesFoldStateLoggingProvider(
+ optionalFoldStateProvider: Optional<FoldStateProvider>
+ ): Optional<FoldStateLoggingProvider> =
+ optionalFoldStateProvider.map { foldStateProvider ->
+ FoldStateLoggingProviderImpl(foldStateProvider, SystemClockImpl())
+ }
+
+ @Provides
+ @Singleton
fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
createConfig(context)
@@ -77,13 +111,9 @@ class UnfoldTransitionModule {
context: Context,
windowManager: IWindowManager,
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
- ) =
- unfoldTransitionProgressProvider.map {
- provider -> NaturalRotationUnfoldProgressProvider(
- context,
- windowManager,
- provider
- )
+ ): Optional<NaturalRotationUnfoldProgressProvider> =
+ unfoldTransitionProgressProvider.map { provider ->
+ NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
}
@Provides
@@ -91,10 +121,8 @@ class UnfoldTransitionModule {
@Singleton
fun provideStatusBarScopedTransitionProvider(
source: Optional<NaturalRotationUnfoldProgressProvider>
- ) =
- source.map {
- provider -> ScopedUnfoldTransitionProgressProvider(provider)
- }
+ ): Optional<ScopedUnfoldTransitionProgressProvider> =
+ source.map { provider -> ScopedUnfoldTransitionProgressProvider(provider) }
@Provides
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 2e183b38a7dc..ba9b638fac99 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -223,8 +223,7 @@ public class WalletScreenController implements
}
mUiEventLogger.log(WalletUiEvent.QAW_CLICK_CARD);
- mActivityStarter.startActivity(
- ((QAWalletCardViewInfo) cardInfo).mWalletCard.getPendingIntent().getIntent(), true);
+ mActivityStarter.startPendingIntentDismissingKeyguard(cardInfo.getPendingIntent());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e8f6de76138d..5e9579cb6a83 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -761,7 +761,7 @@ public class BubblesManager implements Dumpable {
}
static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
- return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+ return new BubbleEntry(e.getSbn(), e.getRanking(), e.isDismissable(),
e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
e.shouldSuppressPeek());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 8fdcaddc93fb..5ad651728c66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
@@ -60,6 +61,7 @@ import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -75,6 +77,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -89,8 +92,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
- private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
- @Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MirrorWindowControl mMirrorWindowControl;
@@ -101,6 +102,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
+ private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@@ -125,10 +127,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
return null;
}).when(mHandler).post(
any(Runnable.class));
-
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
mResources = getContext().getOrCreateTestableResources().getResources();
+ mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+ mContext);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
@@ -157,6 +160,52 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
+ public void enableWindowMagnification_notifySourceBoundsChanged() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
+ /* magnificationFrameOffsetRatioY= */ 0, null);
+ });
+
+ // Waits for the surface created
+ verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
+ (eq(mContext.getDisplayId())), any());
+ }
+
+ @Test
+ public void enableWindowMagnification_withAnimation_schedulesFrame() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
+ 10, /* magnificationFrameOffsetRatioX= */ 0,
+ /* magnificationFrameOffsetRatioY= */ 0,
+ Mockito.mock(IRemoteMagnificationAnimationCallback.class));
+ });
+
+ verify(mSfVsyncFrameProvider,
+ timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any());
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN, 0, 0, null);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.moveWindowMagnifier(10, 10);
+ });
+
+ final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+ verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
+ (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+ assertEquals(mWindowMagnificationController.getCenterX(),
+ sourceBoundsCaptor.getValue().exactCenterX(), 0);
+ assertEquals(mWindowMagnificationController.getCenterY(),
+ sourceBoundsCaptor.getValue().exactCenterY(), 0);
+ }
+
+ @Test
public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index c898150d857c..343658d31272 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -149,6 +149,16 @@ public class WindowMagnificationTest extends SysuiTestCase {
}
@Test
+ public void onDrag_enabled_notifyCallback() throws RemoteException {
+ mCommandQueue.requestWindowMagnificationConnection(true);
+ waitForIdleSync();
+
+ mWindowMagnification.onDrag(TEST_DISPLAY);
+
+ verify(mConnectionCallback).onDrag(TEST_DISPLAY);
+ }
+
+ @Test
public void onConfigurationChanged_updateModeSwitches() {
final Configuration config = new Configuration();
config.densityDpi = Configuration.DENSITY_DPI_ANY;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 8cc2776bc16b..43d9a755269f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView
+import com.android.systemui.statusbar.notification.stack.MediaContainerView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.UniqueObjectHostView
@@ -57,7 +57,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
@JvmField @Rule
val mockito = MockitoJUnit.rule()
- private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null)
+ private val mediaContainerView: MediaContainerView = MediaContainerView(context, null)
private val hostView = UniqueObjectHostView(context)
private lateinit var keyguardMediaController: KeyguardMediaController
@@ -78,7 +78,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
context,
configurationController
)
- keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
+ keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
keyguardMediaController.useSplitShade = false
}
@@ -88,7 +88,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+ assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@Test
@@ -102,7 +102,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
private fun testStateVisibility(state: Int, visibility: Int) {
whenever(statusBarStateController.state).thenReturn(state)
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(visibility)
+ assertThat(mediaContainerView.visibility).isEqualTo(visibility)
}
@Test
@@ -112,7 +112,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
keyguardMediaController.refreshMediaPosition()
- assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
+ assertThat(mediaContainerView.visibility).isEqualTo(GONE)
}
@Test
@@ -130,7 +130,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
- assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE)
+ assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
@Test
@@ -149,6 +149,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
assertTrue("HostView wasn't attached to the single pane container",
- mediaHeaderView.childCount == 1)
+ mediaContainerView.childCount == 1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index 8cd7d94d8952..5a0604883863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -100,4 +100,34 @@ public class ColorSchemeTest extends SysuiTestCase {
Cam cam = Cam.fromInt(tertiaryMid);
Assert.assertEquals(cam.getHue(), 50.0, 10.0);
}
+
+ @Test
+ public void testSpritz() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.SPRITZ /* style */);
+ int primaryMid = colorScheme.getAccent1().get(colorScheme.getAccent1().size() / 2);
+ Cam cam = Cam.fromInt(primaryMid);
+ Assert.assertEquals(cam.getChroma(), 4.0, 1.0);
+ }
+
+ @Test
+ public void testVibrant() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.VIBRANT /* style */);
+ int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ Cam cam = Cam.fromInt(neutralMid);
+ Assert.assertEquals(cam.getChroma(), 8.0, 1.0);
+ }
+
+ @Test
+ public void testExpressive() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */,
+ Style.EXPRESSIVE /* style */);
+ int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
+ Cam cam = Cam.fromInt(neutralMid);
+ Assert.assertEquals(cam.getChroma(), 16.0, 1.0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 2e1fb07e6aa5..8ccf5596b0ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -218,7 +218,8 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
String expected = "TestableQSPanelControllerBase:\n"
+ " Tile records:\n"
+ " " + mockTileString + "\n"
- + " " + mockTileViewString + "\n";
+ + " " + mockTileViewString + "\n"
+ + " media bounds: null\n";
assertEquals(expected, w.getBuffer().toString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 5de4c115ee56..6c29ecc7ae50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -151,17 +151,17 @@ public class CommandQueueTest extends SysuiTestCase {
@Test
public void testShowTransient() {
int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
- mCommandQueue.showTransient(DEFAULT_DISPLAY, types);
+ mCommandQueue.showTransient(DEFAULT_DISPLAY, types, true /* isGestureOnSystemBar */);
waitForIdleSync();
- verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types));
+ verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types), eq(true));
}
@Test
public void testShowTransientForSecondaryDisplay() {
int[] types = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
- mCommandQueue.showTransient(SECONDARY_DISPLAY, types);
+ mCommandQueue.showTransient(SECONDARY_DISPLAY, types, true /* isGestureOnSystemBar */);
waitForIdleSync();
- verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types));
+ verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types), eq(true));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
index 38ad6b85f8aa..c0d1155b2700 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -37,7 +37,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags)
@Test
- fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() {
+ fun getDisableFlagsString_oldAndNewSame_newAndUnchangedLoggedOldNotLogged() {
val state = DisableFlagsLogger.DisableState(
0b111, // ABC
0b01 // mN
@@ -45,10 +45,9 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
val result = disableFlagsLogger.getDisableFlagsString(state, state)
- assertThat(result).contains("Old: ABC.mN")
- assertThat(result).contains("New: ABC.mN")
- assertThat(result).doesNotContain("(")
- assertThat(result).doesNotContain(")")
+ assertThat(result).doesNotContain("Old")
+ assertThat(result).contains("ABC.mN")
+ assertThat(result).contains("(unchanged)")
}
@Test
@@ -66,7 +65,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
assertThat(result).contains("Old: ABC.mN")
assertThat(result).contains("New: abC.Mn")
- assertThat(result).contains("(ab.Mn)")
+ assertThat(result).contains("(changed: ab.Mn)")
}
@Test
@@ -82,7 +81,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
)
)
- assertThat(result).contains("(.n)")
+ assertThat(result).contains("(changed: .n)")
}
@Test
@@ -96,8 +95,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
)
assertThat(result).doesNotContain("Old")
- assertThat(result).contains("New: abC.mN")
- // We have no state to diff on, so we shouldn't see any diff in parentheses
+ assertThat(result).contains("abC.mN")
assertThat(result).doesNotContain("(")
assertThat(result).doesNotContain(")")
}
@@ -141,7 +139,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() {
)
)
- assertThat(result).contains("local modification: Abc.Mn (A.M)")
+ assertThat(result).contains("local modification: Abc.Mn (changed: A.M)")
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 89435ae164b5..13b8e81a0711 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -9,6 +9,8 @@ import com.android.systemui.ExpandHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
@@ -18,7 +20,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.phone.StatusBar
@@ -56,7 +58,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
- @Mock lateinit var lockscreenGestureLogger: LockscreenGestureLogger
+ @Mock lateinit var logger: LSShadeTransitionLogger
+ @Mock lateinit var dumpManager: DumpManager
@Mock lateinit var keyguardBypassController: KeyguardBypassController
@Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock lateinit var falsingCollector: FalsingCollector
@@ -66,6 +69,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var scrimController: ScrimController
@Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var buffer: LogBuffer
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@Mock lateinit var depthController: NotificationShadeDepthController
@@ -86,18 +90,18 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
.addOverride(R.bool.config_use_split_notification_shade, false)
transitionController = LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
- lockscreenGestureLogger = lockscreenGestureLogger,
+ logger = logger,
keyguardBypassController = keyguardBypassController,
lockScreenUserManager = lockScreenUserManager,
falsingCollector = falsingCollector,
ambientState = ambientState,
- displayMetrics = displayMetrics,
mediaHierarchyManager = mediaHierarchyManager,
scrimController = scrimController,
depthController = depthController,
context = context,
configurationController = configurationController,
- falsingManager = falsingManager
+ falsingManager = falsingManager,
+ dumpManager = dumpManager
)
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 41163bf433c1..a7f8b6e01949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.notification.collection;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -797,7 +799,7 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
- public void testDismissingSummaryDoesNotDismissForegroundServiceChildren() {
+ public void testDismissingSummaryDoesDismissForegroundServiceChildren() {
// GIVEN a collection with three grouped notifs in it
CollectionEvent notif0 = postNotif(
buildNotif(TEST_PACKAGE, 0)
@@ -814,7 +816,31 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN the summary is dismissed
mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
- // THEN the foreground service child is not dismissed
+ // THEN the foreground service child is dismissed
+ assertEquals(DISMISSED, notif0.entry.getDismissState());
+ assertEquals(PARENT_DISMISSED, notif1.entry.getDismissState());
+ assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
+ }
+
+ @Test
+ public void testDismissingSummaryDoesNotDismissOngoingChildren() {
+ // GIVEN a collection with three grouped notifs in it
+ CollectionEvent notif0 = postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ CollectionEvent notif1 = postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, GROUP_1)
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true));
+ CollectionEvent notif2 = postNotif(
+ buildNotif(TEST_PACKAGE, 2)
+ .setGroup(mContext, GROUP_1));
+
+ // WHEN the summary is dismissed
+ mCollection.dismissNotification(notif0.entry, defaultStats(notif0.entry));
+
+ // THEN the ongoing child is not dismissed
assertEquals(DISMISSED, notif0.entry.getDismissState());
assertEquals(NOT_DISMISSED, notif1.entry.getDismissState());
assertEquals(PARENT_DISMISSED, notif2.entry.getDismissState());
@@ -1427,6 +1453,37 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mCollectionListener, never()).onEntryUpdated(any(), anyBoolean());
}
+ @Test
+ public void testCannotDismissOngoingNotificationChildren() {
+ // GIVEN an ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .setGroup(mContext, "group")
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+ .build();
+
+ // THEN its children are not dismissible
+ assertFalse(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
+ public void testCanDismissFgsNotificationChildren() {
+ // GIVEN an FGS but not ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .setGroup(mContext, "group")
+ .setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ container.setDismissState(NOT_DISMISSED);
+
+ // THEN its children are dismissible
+ assertTrue(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
return new NotificationEntryBuilder()
.setPkg(pkg)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 5271745a2b44..f77381000ae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -31,18 +32,18 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
-import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
class NodeSpecBuilderTest : SysuiTestCase() {
- @Mock
- private lateinit var viewBarn: NotifViewBarn
+ private val mediaContainerController: MediaContainerController = mock()
+ private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
+ private val viewBarn: NotifViewBarn = mock()
private var rootController: NodeController = buildFakeController("rootController")
private var headerController0: NodeController = buildFakeController("header0")
@@ -66,13 +67,12 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- `when`(viewBarn.requireNodeController(any())).thenAnswer {
+ whenever(mediaContainerController.mediaContainerView).thenReturn(mock())
+ whenever(viewBarn.requireNodeController(any())).thenAnswer {
fakeViewBarn.getViewByEntry(it.getArgument(0))
}
- specBuilder = NodeSpecBuilder(viewBarn)
+ specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager, viewBarn)
}
@Test
@@ -109,6 +109,30 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test
fun testSimpleMapping() {
checkOutput(
+ // GIVEN a simple flat list of notifications all in the same headerless section
+ listOf(
+ notif(0, section0NoHeader),
+ notif(1, section0NoHeader),
+ notif(2, section0NoHeader),
+ notif(3, section0NoHeader)
+ ),
+
+ // THEN we output a similarly simple flag list of nodes
+ tree(
+ notifNode(0),
+ notifNode(1),
+ notifNode(2),
+ notifNode(3)
+ )
+ )
+ }
+
+ @Test
+ fun testSimpleMappingWithMedia() {
+ // WHEN media controls are enabled
+ whenever(sectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true)
+
+ checkOutput(
// GIVEN a simple flat list of notifications all in the same headerless section
listOf(
notif(0, section0NoHeader),
@@ -117,8 +141,9 @@ class NodeSpecBuilderTest : SysuiTestCase() {
notif(3, section0NoHeader)
),
- // THEN we output a similarly simple flag list of nodes
+ // THEN we output a similarly simple flag list of nodes, with media at the top
tree(
+ node(mediaContainerController),
notifNode(0),
notifNode(1),
notifNode(2),
@@ -333,7 +358,7 @@ private class FakeViewBarn {
private fun buildFakeController(name: String): NodeController {
val controller = Mockito.mock(NodeController::class.java)
- `when`(controller.nodeLabel).thenReturn(name)
+ whenever(controller.nodeLabel).thenReturn(name)
return controller
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e9e191107e5c..4ea932157457 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.notification.row;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static org.junit.Assert.assertEquals;
@@ -29,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -330,4 +334,28 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertTrue(row.getIsNonblockable());
}
+
+ @Test
+ public void testCanDismissNoClear() throws Exception {
+ ExpandableNotificationRow row =
+ mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+ modifySbn(row.getEntry())
+ .setFlag(mContext, FLAG_NO_CLEAR, true)
+ .build();
+ row.performDismiss(false);
+ verify(mNotificationTestHelper.mOnUserInteractionCallback)
+ .onDismiss(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testCannotDismissOngoing() throws Exception {
+ ExpandableNotificationRow row =
+ mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
+ modifySbn(row.getEntry())
+ .setFlag(mContext, FLAG_ONGOING_EVENT, true)
+ .build();
+ row.performDismiss(false);
+ verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
+ .onDismiss(any(), anyInt(), any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 9039e1b8f8e3..1f92b0a42061 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -57,7 +57,7 @@ public class FooterViewTest extends SysuiTestCase {
@Test
public void setDismissOnClick() {
- mView.setDismissButtonClickListener(mock(View.OnClickListener.class));
+ mView.setClearAllButtonClickListener(mock(View.OnClickListener.class));
assertTrue(mView.findSecondaryView().hasOnClickListeners());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index e4721b13332c..4457ae046260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -115,6 +115,7 @@ public class NotificationTestHelper {
private final IconManager mIconManager;
private StatusBarStateController mStatusBarStateController;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ public final OnUserInteractionCallback mOnUserInteractionCallback;
public NotificationTestHelper(
Context context,
@@ -173,6 +174,7 @@ public class NotificationTestHelper {
verify(collection).addCollectionListener(collectionListenerCaptor.capture());
mBindPipelineEntryListener = collectionListenerCaptor.getValue();
mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
+ mOnUserInteractionCallback = mock(OnUserInteractionCallback.class);
}
/**
@@ -499,7 +501,7 @@ public class NotificationTestHelper {
new FalsingCollectorFake(),
mStatusBarStateController,
mPeopleNotificationIdentifier,
- mock(OnUserInteractionCallback.class),
+ mOnUserInteractionCallback,
Optional.of(mock(BubblesManager.class)),
mock(NotificationGutsManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 4d2c0c306c00..ac9fcc064375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -41,7 +41,6 @@ import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -54,6 +53,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.MediaContainerController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -87,6 +87,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private NotificationRowComponent mNotificationRowComponent;
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@Mock private NotificationSectionsLogger mLogger;
+ @Mock private MediaContainerController mMediaContainerController;
@Mock private SectionHeaderController mIncomingHeaderController;
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@@ -114,6 +115,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
});
when(mNotificationRowComponent.getActivatableNotificationViewController())
.thenReturn(mActivatableNotificationViewController);
+ when(mMediaContainerController.getMediaContainerView())
+ .thenReturn(mock(MediaContainerView.class));
when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
@@ -126,6 +129,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsFeatureManager,
mLogger,
mNotifPipelineFlags,
+ mMediaContainerController,
mIncomingHeaderController,
mPeopleHeaderController,
mAlertingHeaderController,
@@ -134,7 +138,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+ mSectionsManager.initialize(mNssl);
when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -142,7 +146,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Test(expected = IllegalStateException.class)
public void testDuplicateInitializeThrows() {
- mSectionsManager.initialize(mNssl, LayoutInflater.from(mContext));
+ mSectionsManager.initialize(mNssl);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 4cc1be696637..04c6f6c63927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -379,18 +379,18 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Test
public void testDismissListener() {
- ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
+ ArgumentCaptor<NotificationStackScrollLayout.ClearAllListener>
dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
- NotificationStackScrollLayout.DismissListener.class);
+ NotificationStackScrollLayout.ClearAllListener.class);
mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout).setDismissListener(
+ verify(mNotificationStackScrollLayout).setClearAllListener(
dismissListenerArgumentCaptor.capture());
- NotificationStackScrollLayout.DismissListener dismissListener =
+ NotificationStackScrollLayout.ClearAllListener dismissListener =
dismissListenerArgumentCaptor.getValue();
- dismissListener.onDismiss(ROWS_ALL);
+ dismissListener.onClearAll(ROWS_ALL);
verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index eda0e83d7eb3..46ba09795143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -447,7 +447,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void testClearNotifications_All() {
final int[] numCalls = {0};
final int[] selected = {-1};
- mStackScroller.setDismissListener(selectedRows -> {
+ mStackScroller.setClearAllListener(selectedRows -> {
numCalls[0]++;
selected[0] = selectedRows;
});
@@ -461,7 +461,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
public void testClearNotifications_Gentle() {
final int[] numCalls = {0};
final int[] selected = {-1};
- mStackScroller.setDismissListener(selectedRows -> {
+ mStackScroller.setClearAllListener(selectedRows -> {
numCalls[0]++;
selected[0] = selectedRows;
});
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 5ada6d4239d1..35f671bf8298 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
@@ -455,7 +455,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mStatusBarStateController,
mFalsingManager,
mLockscreenShadeTransitionController,
- new FalsingCollectorFake());
+ new FalsingCollectorFake(),
+ mDumpManager);
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 34407b0440c6..f804d83a66a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -39,6 +39,7 @@ import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayIdentifier;
+import android.database.ContentObserver;
import android.graphics.Color;
import android.os.Handler;
import android.os.UserHandle;
@@ -55,6 +56,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.monet.Style;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -117,6 +119,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
@Captor
private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
+ @Captor
+ private ArgumentCaptor<ContentObserver> mSettingsObserver;
+ private Style mCurrentStyle;
@Before
public void setup() {
@@ -130,10 +135,11 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
+ mCurrentStyle = style;
return overlay;
}
};
@@ -148,6 +154,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
verify(mDumpManager).registerDumpable(any(), any());
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
+ verify(mSecureSettings).registerContentObserverForUser(
+ eq(Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)),
+ eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
+ );
}
@Test
@@ -211,12 +221,6 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
verify(mThemeOverlayApplier)
.applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
- // Assert that we received the colors that we were expecting
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
- .isEqualTo(new OverlayIdentifier("ffff0000"));
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(new OverlayIdentifier("ffff0000"));
-
// Should not change theme after changing wallpapers, if intent doesn't have
// WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
clearInvocations(mThemeOverlayApplier);
@@ -322,6 +326,40 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
}
@Test
+ public void onSettingChanged_honorThemeStyle() {
+ when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+ for (Style style : Style.values()) {
+ reset(mSecureSettings);
+
+ String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.theme_style\":\"" + style.name() + "\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+ assertThat(mCurrentStyle).isEqualTo(style);
+ }
+ }
+
+ @Test
+ public void onSettingChanged_invalidStyle() {
+ when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
+ String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
+ + "\"android.theme.customization.theme_style\":\"some_invalid_name\"}";
+
+ when(mSecureSettings.getStringForUser(
+ eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+ .thenReturn(jsonString);
+
+ mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
+
+ assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT);
+ }
+
+ @Test
public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -611,11 +649,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));
-
return overlay;
}
@@ -648,7 +685,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) {
@Nullable
@Override
- protected FabricatedOverlay getOverlay(int color, int type) {
+ protected FabricatedOverlay getOverlay(int color, int type, Style style) {
FabricatedOverlay overlay = mock(FabricatedOverlay.class);
when(overlay.getIdentifier())
.thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
new file mode 100644
index 000000000000..8076b4eda883
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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.unfold
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateLoggingProviderTest : SysuiTestCase() {
+
+ @Captor
+ private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener>
+
+ @Mock private lateinit var foldStateProvider: FoldStateProvider
+
+ private val fakeClock = FakeSystemClock()
+
+ private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
+
+ private val foldLoggingUpdates: MutableList<FoldStateChange> = arrayListOf()
+
+ private val foldStateLoggingListener =
+ object : FoldStateLoggingListener {
+ override fun onFoldUpdate(foldStateUpdate: FoldStateChange) {
+ foldLoggingUpdates.add(foldStateUpdate)
+ }
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ foldStateLoggingProvider =
+ FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply {
+ addCallback(foldStateLoggingListener)
+ init()
+ }
+
+ verify(foldStateProvider).addCallback(foldUpdatesListener.capture())
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishHalfOpen_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishFullOpen_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_noPreviousOne_finishClosed_nothingReported() {
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_startOpening_fullOpen_changeReported() {
+ val dtTime = 10L
+
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_CLOSED, FULLY_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenFinishClosed_noInitialState_nothingReported() {
+ val dtTime = 10L
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates).isEmpty()
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenFinishClosed_initiallyOpened_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_OPENED, FULLY_CLOSED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startOpeningThenHalf_initiallyClosed_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_CLOSED, HALF_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_startClosingThenHalf_initiallyOpened_changeReported() {
+ val dtTime = 10L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(dtTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(FoldStateChange(FULLY_OPENED, HALF_OPENED, dtTime))
+ }
+
+ @Test
+ fun onFoldUpdate_foldThenUnfold_multipleReported() {
+ val foldTime = 24L
+ val unfoldTime = 42L
+ val waitingTime = 424L
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ // Fold
+ sendFoldUpdate(FOLD_UPDATE_START_CLOSING)
+ fakeClock.advanceTime(foldTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ fakeClock.advanceTime(waitingTime)
+ // unfold
+ sendFoldUpdate(FOLD_UPDATE_START_OPENING)
+ fakeClock.advanceTime(unfoldTime)
+ sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN)
+
+ assertThat(foldLoggingUpdates)
+ .containsExactly(
+ FoldStateChange(FULLY_OPENED, FULLY_CLOSED, foldTime),
+ FoldStateChange(FULLY_CLOSED, FULLY_OPENED, unfoldTime))
+ }
+
+ @Test
+ fun uninit_removesCallback() {
+ foldStateLoggingProvider.uninit()
+
+ verify(foldStateProvider).removeCallback(foldUpdatesListener.value)
+ }
+
+ private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
+ foldUpdatesListener.value.onFoldUpdate(foldUpdate)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 46e0b1238013..f43dc6c5bc83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
+import java.lang.Exception
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,32 +40,24 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-import java.lang.Exception
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DeviceFoldStateProviderTest : SysuiTestCase() {
- @Mock
- private lateinit var hingeAngleProvider: HingeAngleProvider
+ @Mock private lateinit var hingeAngleProvider: HingeAngleProvider
- @Mock
- private lateinit var screenStatusProvider: ScreenStatusProvider
+ @Mock private lateinit var screenStatusProvider: ScreenStatusProvider
- @Mock
- private lateinit var deviceStateManager: DeviceStateManager
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
- @Mock
- private lateinit var handler: Handler
+ @Mock private lateinit var handler: Handler
- @Captor
- private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+ @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
- @Captor
- private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+ @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
- @Captor
- private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+ @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
private lateinit var foldStateProvider: DeviceFoldStateProvider
@@ -81,24 +74,25 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
deviceStates = FoldableTestUtils.findDeviceStates(context)
- foldStateProvider = DeviceFoldStateProvider(
- context,
- hingeAngleProvider,
- screenStatusProvider,
- deviceStateManager,
- context.mainExecutor,
- handler
- )
-
- foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
- override fun onHingeAngleUpdate(angle: Float) {
- hingeAngleUpdates.add(angle)
- }
-
- override fun onFoldUpdate(update: Int) {
- foldUpdates.add(update)
- }
- })
+ foldStateProvider =
+ DeviceFoldStateProvider(
+ context,
+ hingeAngleProvider,
+ screenStatusProvider,
+ deviceStateManager,
+ context.mainExecutor,
+ handler)
+
+ foldStateProvider.addCallback(
+ object : FoldStateProvider.FoldUpdatesListener {
+ override fun onHingeAngleUpdate(angle: Float) {
+ hingeAngleUpdates.add(angle)
+ }
+
+ override fun onFoldUpdate(update: Int) {
+ foldUpdates.add(update)
+ }
+ })
foldStateProvider.start()
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
@@ -200,9 +194,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent(90)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS)
- assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ assertThat(foldUpdates)
+ .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
}
@Test
@@ -210,7 +205,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent(90)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
@@ -220,7 +215,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent(180)
sendHingeAngleEvent(90)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1)
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
sendHingeAngleEvent(80)
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
@@ -231,11 +226,13 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
sendHingeAngleEvent(180)
sendHingeAngleEvent(90)
- simulateTimeout(ABORT_CLOSING_MILLIS - 1) // The timeout should not trigger here.
+ // The timeout should not trigger here.
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
sendHingeAngleEvent(80)
- simulateTimeout(ABORT_CLOSING_MILLIS) // The timeout should trigger here.
+ simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) // The timeout should trigger here.
- assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_ABORTED)
+ assertThat(foldUpdates)
+ .containsExactly(FOLD_UPDATE_START_CLOSING, FOLD_UPDATE_FINISH_HALF_OPEN)
}
@Test
@@ -261,7 +258,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
}
- private fun simulateTimeout(waitTime: Long = ABORT_CLOSING_MILLIS) {
+ private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
val runnableDelay = scheduledRunnableDelay ?: throw Exception("No runnable scheduled.")
if (waitTime >= runnableDelay) {
scheduledRunnable?.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index e3b07b3e8bd7..01769e52c8d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -21,7 +21,6 @@ import static android.view.View.VISIBLE;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -96,7 +95,7 @@ public class WalletScreenControllerTest extends SysuiTestCase {
@Mock
UiEventLogger mUiEventLogger;
@Captor
- ArgumentCaptor<Intent> mIntentCaptor;
+ ArgumentCaptor<PendingIntent> mIntentCaptor;
@Captor
ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
private WalletScreenController mController;
@@ -374,10 +373,12 @@ public class WalletScreenControllerTest extends SysuiTestCase {
mController.onCardClicked(walletCardViewInfo);
- verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true));
+ verify(mActivityStarter).startPendingIntentDismissingKeyguard(mIntentCaptor.capture());
- assertEquals(mWalletIntent.getAction(), mIntentCaptor.getValue().getAction());
- assertEquals(mWalletIntent.getComponent(), mIntentCaptor.getValue().getComponent());
+ Intent actualIntent = mIntentCaptor.getValue().getIntent();
+
+ assertEquals(mWalletIntent.getAction(), actualIntent.getAction());
+ assertEquals(mWalletIntent.getComponent(), actualIntent.getComponent());
verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_CLICK_CARD);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index ad3e1d56c51b..e93ac47b1940 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1036,6 +1036,30 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
+
+ @Override
+ public Region getCurrentMagnificationRegion(int displayId) {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getCurrentMagnificationRegion", "displayId=" + displayId);
+ }
+ synchronized (mLock) {
+ final Region region = Region.obtain();
+ if (!hasRightsToCurrentUserLocked()) {
+ return region;
+ }
+ MagnificationProcessor magnificationProcessor =
+ mSystemSupport.getMagnificationProcessor();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ magnificationProcessor.getCurrentMagnificationRegion(displayId,
+ region, mSecurityPolicy.canControlMagnification(this));
+ return region;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
@Override
public float getMagnificationCenterX(int displayId) {
if (svcConnTracingEnabled()) {
@@ -1103,6 +1127,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
@Override
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("resetCurrentMagnification",
+ "displayId=" + displayId + ";animate=" + animate);
+ }
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return false;
+ }
+ if (!mSecurityPolicy.canControlMagnification(this)) {
+ return false;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ MagnificationProcessor magnificationProcessor =
+ mSystemSupport.getMagnificationProcessor();
+ return (magnificationProcessor.resetCurrentMagnification(displayId, animate)
+ || !magnificationProcessor.isMagnifying(displayId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean setMagnificationConfig(int displayId,
@NonNull MagnificationConfig config, boolean animate) {
if (svcConnTracingEnabled()) {
@@ -1542,9 +1591,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
mInvocationHandler
- .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ .notifyMagnificationChangedLocked(displayId, region, config);
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
@@ -1564,15 +1613,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* state of magnification has changed.
*/
private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
if (svcClientTracingEnabled()) {
logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
- + scale + ", " + centerX + ", " + centerY);
+ + config.toString());
}
- listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
+ listener.onMagnificationChanged(displayId, region, config);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
}
@@ -1899,11 +1948,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
case MSG_ON_MAGNIFICATION_CHANGED: {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
- final float scale = (float) args.arg2;
- final float centerX = (float) args.arg3;
- final float centerY = (float) args.arg4;
+ final MagnificationConfig config = (MagnificationConfig) args.arg2;
final int displayId = args.argi1;
- notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
+ notifyMagnificationChangedInternal(displayId, region, config);
args.recycle();
} break;
@@ -1932,7 +1979,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
synchronized (mLock) {
if (mMagnificationCallbackState.get(displayId) == null) {
return;
@@ -1941,9 +1988,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
- args.arg2 = scale;
- args.arg3 = centerX;
- args.arg4 = centerY;
+ args.arg2 = config;
args.argi1 = displayId;
final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e59a3d633cfd..aa69a09034fc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,6 +44,7 @@ import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.MagnificationConfig;
import android.accessibilityservice.TouchInteractionController;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1301,18 +1302,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Called by the MagnificationController when the state of display
* magnification changes.
*
- * @param displayId The logical display id.
+ * <p>
+ * It can notify window magnification change if the service supports controlling all the
+ * magnification mode.
+ * </p>
+ *
+ * @param displayId The logical display id
* @param region the new magnified region, may be empty if
* magnification is not enabled (e.g. scale is 1)
- * @param scale the new scale
- * @param centerX the new screen-relative center X coordinate
- * @param centerY the new screen-relative center Y coordinate
+ * @param config The magnification config. That has magnification mode, the new scale and the
+ * new screen-relative center position
*/
public void notifyMagnificationChanged(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
synchronized (mLock) {
notifyClearAccessibilityCacheLocked();
- notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ notifyMagnificationChangedLocked(displayId, region, config);
}
}
@@ -1613,11 +1618,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
- float scale, float centerX, float centerY) {
+ @NonNull MagnificationConfig config) {
final AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
+ service.notifyMagnificationChangedLocked(displayId, region, config);
}
}
@@ -2393,6 +2398,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
somethingChanged |= readMagnificationCapabilitiesLocked(userState);
+ somethingChanged |= readMagnificationFollowTypingLocked(userState);
return somethingChanged;
}
@@ -3868,6 +3874,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mMagnificationCapabilityUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
+ private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -3906,6 +3915,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mMagnificationModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mMagnificationCapabilityUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -3973,6 +3984,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (readMagnificationCapabilitiesLocked(userState)) {
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
}
+ } else if (mMagnificationFollowTypingUri.equals(uri)) {
+ readMagnificationFollowTypingLocked(userState);
}
}
}
@@ -4069,6 +4082,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
+ boolean readMagnificationFollowTypingLocked(AccessibilityUserState userState) {
+ final boolean followTypeEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ 1, userState.mUserId) == 1;
+ if (followTypeEnabled != userState.isMagnificationFollowTypingEnabled()) {
+ userState.setMagnificationFollowTypingEnabled(followTypeEnabled);
+ mMagnificationController.setMagnificationFollowTypingEnabled(followTypeEnabled);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
mMainHandler.sendMessage(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 9324e3e0db47..0f354561faaf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -129,6 +129,8 @@ class AccessibilityUserState {
private final SparseIntArray mMagnificationModes = new SparseIntArray();
// The magnification capabilities used to know magnification mode could be switched.
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
/** The stroke width of the focus rectangle in pixels */
private int mFocusStrokeWidth;
@@ -210,6 +212,7 @@ class AccessibilityUserState {
mMagnificationModes.clear();
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
+ mMagnificationFollowTypingEnabled = true;
}
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -685,6 +688,14 @@ class AccessibilityUserState {
mMagnificationCapabilities = capabilities;
}
+ public void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
+ public boolean isMagnificationFollowTypingEnabled() {
+ return mMagnificationFollowTypingEnabled;
+ }
+
/**
* Sets the magnification mode to the given display.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 72bc8503b7a5..e39b979643ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -17,10 +17,12 @@
package com.android.server.accessibility.magnification;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
+import android.accessibilityservice.MagnificationConfig;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -89,6 +91,8 @@ public class FullScreenMagnificationController implements
private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
private final Rect mTempRect = new Rect();
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
/**
* This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
@@ -363,9 +367,16 @@ public class FullScreenMagnificationController implements
return mIdOfLastServiceToMagnify;
}
+ @GuardedBy("mLock")
void onMagnificationChangedLocked() {
- mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
- getScale(), getCenterX(), getCenterY());
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
+ .setScale(getScale())
+ .setCenterX(getCenterX())
+ .setCenterY(getCenterY()).build();
+ mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId,
+ mMagnificationRegion,
+ config);
if (mUnregisterPending && !isMagnifying()) {
unregister(mDeleteAfterUnregister);
}
@@ -735,6 +746,9 @@ public class FullScreenMagnificationController implements
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
int bottom) {
synchronized (mLock) {
+ if (!mMagnificationFollowTypingEnabled) {
+ return;
+ }
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return;
@@ -751,6 +765,10 @@ public class FullScreenMagnificationController implements
}
}
+ void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
/**
* Remove the display magnification with given id.
*
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 037dc1f6795c..b70ffb2243e1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -379,6 +380,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mAms.changeMagnificationMode(displayId, magnificationMode);
}
+ @Override
+ public void onSourceBoundsChanged(int displayId, Rect bounds) {
+ final MagnificationConfig config = new MagnificationConfig.Builder()
+ .setMode(MAGNIFICATION_MODE_WINDOW)
+ .setScale(mScaleProvider.getScale(displayId))
+ .setCenterX(bounds.exactCenterX())
+ .setCenterY(bounds.exactCenterY()).build();
+ mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
+ }
+
private void disableFullScreenMagnificationIfNeeded(int displayId) {
final FullScreenMagnificationController fullScreenMagnificationController =
getFullScreenMagnificationController();
@@ -426,6 +437,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
mImeWindowVisible = shown;
}
+ getWindowMagnificationMgr().onImeWindowVisibilityChanged(shown);
logMagnificationModeWithImeOnIfNeeded();
}
@@ -518,6 +530,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mMagnificationCapabilities = capabilities;
}
+ /**
+ * Called when the following typing focus feature is switched.
+ *
+ * @param enabled Enable the following typing focus feature
+ */
+ public void setMagnificationFollowTypingEnabled(boolean enabled) {
+ getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+ getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
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 40f77b04d5de..8f15d5cf3e3d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -203,6 +203,36 @@ public class MagnificationProcessor {
}
/**
+ * Returns the region of the screen currently active for magnification if the
+ * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
+ * Returns the region of screen projected on the magnification window if the controlling
+ * magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
+ * <p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
+ * the returned region will be empty if the magnification is
+ * not active. And the magnification is active if magnification gestures are enabled
+ * or if a service is running that can control magnification.
+ * </p><p>
+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
+ * the returned region will be empty if the magnification is not activated.
+ * </p>
+ *
+ * @param displayId The logical display id
+ * @param outRegion the region to populate
+ * @param canControlMagnification Whether the service can control magnification
+ */
+ public void getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion,
+ boolean canControlMagnification) {
+ int currentMode = getControllingMode(displayId);
+ if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
+ getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
+ } else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
+ mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ outRegion);
+ }
+ }
+
+ /**
* Returns the magnification bounds of full-screen magnification on the given display.
*
* @param displayId The logical display id
@@ -224,8 +254,8 @@ public class MagnificationProcessor {
}
/**
- * Resets the current magnification on the given display. The reset mode could be
- * full-screen or window if it is activated.
+ * Resets the controlling magnifier on the given display.
+ * For resetting window magnifier, it disables the magnifier by setting the scale to 1.
*
* @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
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 c4a577d6e461..95c7d446d75e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -97,6 +97,8 @@ public class WindowMagnificationManager implements
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
+ // Whether the following typing focus feature for magnification is enabled.
+ private boolean mMagnificationFollowTypingEnabled = true;
private boolean mReceiverRegistered = false;
@VisibleForTesting
@@ -139,6 +141,14 @@ public class WindowMagnificationManager implements
void onWindowMagnificationActivationState(int displayId, boolean activated);
/**
+ * Called when the magnification source bounds are changed.
+ *
+ * @param displayId The logical display id.
+ * @param bounds The magnified source bounds on the display.
+ */
+ void onSourceBoundsChanged(int displayId, Rect bounds);
+
+ /**
* Called from {@link IWindowMagnificationConnection} to request changing the magnification
* mode on the given display.
*
@@ -291,13 +301,73 @@ public class WindowMagnificationManager implements
@Override
public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
int bottom) {
- // TODO(b/194668976): We will implement following typing focus in window mode after
- // our refactor.
+ if (!mMagnificationFollowTypingEnabled) {
+ return;
+ }
+
+ float toCenterX = (float) (left + right) / 2;
+ float toCenterY = (float) (top + bottom) / 2;
+
+ if (!isPositionInSourceBounds(displayId, toCenterX, toCenterY)
+ && isTrackingTypingFocusEnabled(displayId)) {
+ enableWindowMagnification(displayId, Float.NaN, toCenterX, toCenterY);
+ }
+ }
+
+ void setMagnificationFollowTypingEnabled(boolean enabled) {
+ mMagnificationFollowTypingEnabled = enabled;
+ }
+
+ /**
+ * Enable or disable tracking typing focus for the specific magnification window.
+ *
+ * The tracking typing focus should be set to enabled with the following conditions:
+ * 1. IME is shown.
+ *
+ * The tracking typing focus should be set to disabled with the following conditions:
+ * 1. A user drags the magnification window by 1 finger.
+ * 2. A user scroll the magnification window by 2 fingers.
+ *
+ * @param displayId The logical display id.
+ * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus.
+ */
+ private void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return;
+ }
+ magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled);
+ }
+ }
+
+ /**
+ * Enable tracking typing focus function for all magnifications.
+ */
+ private void enableAllTrackingTypingFocus() {
+ synchronized (mLock) {
+ for (int i = 0; i < mWindowMagnifiers.size(); i++) {
+ WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
+ magnifier.setTrackingTypingFocusEnabled(true);
+ }
+ }
+ }
+
+ /**
+ * Called when the IME window visibility changed.
+ *
+ * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden.
+ */
+ void onImeWindowVisibilityChanged(boolean shown) {
+ if (shown) {
+ enableAllTrackingTypingFocus();
+ }
}
@Override
public boolean processScroll(int displayId, float distanceX, float distanceY) {
moveWindowMagnification(displayId, -distanceX, -distanceY);
+ setTrackingTypingFocusEnabled(displayId, false);
return /* event consumed: */ true;
}
@@ -469,6 +539,16 @@ public class WindowMagnificationManager implements
}
}
+ boolean isPositionInSourceBounds(int displayId, float x, float y) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ return magnifier.isPositionInSourceBounds(x, y);
+ }
+ }
+
/**
* Indicates whether window magnification is enabled on specified display.
*
@@ -598,6 +678,16 @@ public class WindowMagnificationManager implements
}
}
+ boolean isTrackingTypingFocusEnabled(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return false;
+ }
+ return magnifier.isTrackingTypingFocusEnabled();
+ }
+ }
+
/**
* Populates magnified bounds on the screen. And the populated magnified bounds would be
* empty If window magnifier is not activated.
@@ -689,6 +779,7 @@ public class WindowMagnificationManager implements
}
magnifier.onSourceBoundsChanged(sourceBounds);
}
+ mCallback.onSourceBoundsChanged(displayId, sourceBounds);
}
@Override
@@ -714,6 +805,17 @@ public class WindowMagnificationManager implements
}
@Override
+ public void onDrag(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onDrag",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId);
+ }
+ setTrackingTypingFocusEnabled(displayId, false);
+ }
+
+ @Override
public void binderDied() {
synchronized (mLock) {
Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient);
@@ -749,7 +851,9 @@ public class WindowMagnificationManager implements
private int mIdOfLastServiceToControl = INVALID_SERVICE_ID;
- private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+ private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
+
+ private boolean mTrackingTypingFocusEnabled = true;
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
mDisplayId = displayId;
@@ -790,7 +894,6 @@ public class WindowMagnificationManager implements
}
}
- @GuardedBy("mLock")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
if (!mEnabled) {
@@ -800,6 +903,7 @@ public class WindowMagnificationManager implements
mDisplayId, animationResultCallback)) {
mEnabled = false;
mIdOfLastServiceToControl = INVALID_SERVICE_ID;
+ mTrackingTypingFocusEnabled = false;
return true;
}
return false;
@@ -848,7 +952,18 @@ public class WindowMagnificationManager implements
return count;
}
- @GuardedBy("mLock")
+ boolean isPositionInSourceBounds(float x, float y) {
+ return mSourceBounds.contains((int) x, (int) y);
+ }
+
+ void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
+ mTrackingTypingFocusEnabled = trackingTypingFocusEnabled;
+ }
+
+ boolean isTrackingTypingFocusEnabled() {
+ return mTrackingTypingFocusEnabled;
+ }
+
boolean isEnabled() {
return mEnabled;
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 5aa1c933ad87..c18f5fa2c782 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -19,7 +19,7 @@ package com.android.server.companion;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
-import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -1169,7 +1169,7 @@ public class CompanionDeviceManagerService extends SystemService
} else {
scanner.startScan(
filters,
- new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+ new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_POWER).build(),
mBleScanCallback);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 262933dea27f..bc8da8443a7d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -45,7 +45,6 @@ import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
-import android.bluetooth.IBluetoothLeCallControl;
import android.content.ActivityNotFoundException;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
@@ -1324,15 +1323,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
+ bluetoothProfile);
}
- Intent intent;
- if (bluetoothProfile == BluetoothProfile.HEADSET) {
- intent = new Intent(IBluetoothHeadset.class.getName());
- } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
- intent = new Intent(IBluetoothLeCallControl.class.getName());
- } else {
+ if (bluetoothProfile != BluetoothProfile.HEADSET) {
return false;
}
+ Intent intent = new Intent(IBluetoothHeadset.class.getName());
psc = new ProfileServiceConnections(intent);
if (!psc.bindService()) {
return false;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 780afd86b373..ec018de8f9a4 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3846,7 +3846,7 @@ class StorageManagerService extends IStorageManager.Stub
final boolean primary = false;
final boolean removable = false;
final boolean emulated = true;
- final boolean stub = false;
+ final boolean externallyManaged = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0;
final UserHandle user = new UserHandle(userId);
@@ -3854,7 +3854,8 @@ class StorageManagerService extends IStorageManager.Stub
final String description = mContext.getString(android.R.string.unknownName);
res.add(new StorageVolume(id, path, path, description, primary, removable, emulated,
- stub, allowMassStorage, maxFileSize, user, null /*uuid */, id, envState));
+ externallyManaged, allowMassStorage, maxFileSize, user, null /*uuid */, id,
+ envState));
}
if (!foundPrimary) {
@@ -3869,7 +3870,7 @@ class StorageManagerService extends IStorageManager.Stub
final boolean primary = true;
final boolean removable = primaryPhysical;
final boolean emulated = !primaryPhysical;
- final boolean stub = false;
+ final boolean externallyManaged = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0L;
final UserHandle owner = new UserHandle(userId);
@@ -3878,7 +3879,7 @@ class StorageManagerService extends IStorageManager.Stub
final String state = Environment.MEDIA_REMOVED;
res.add(0, new StorageVolume(id, path, path,
- description, primary, removable, emulated, stub,
+ description, primary, removable, emulated, externallyManaged,
allowMassStorage, maxFileSize, owner, uuid, fsUuid, state));
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 0c990ecfc827..6a7afd90dc96 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,7 +24,6 @@ import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
-import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -164,10 +163,6 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
- // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
-
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -372,12 +367,15 @@ public class VcnManagementService extends IVcnManagementService.Stub {
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- mNetworkProvider.register();
- mContext.getSystemService(ConnectivityManager.class)
- .registerNetworkCallback(
- new NetworkRequest.Builder().clearCapabilities().build(),
- mTrackingNetworkCallback);
- mTelephonySubscriptionTracker.register();
+ // Always run on the handler thread to ensure consistency.
+ mHandler.post(() -> {
+ mNetworkProvider.register();
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
+ mTelephonySubscriptionTracker.register();
+ });
}
private void enforcePrimaryUser() {
@@ -471,22 +469,15 @@ public class VcnManagementService extends IVcnManagementService.Stub {
if (!mVcns.containsKey(subGrp)) {
startVcnLocked(subGrp, entry.getValue());
}
-
- // Cancel any scheduled teardowns for active subscriptions
- mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
}
}
- // Schedule teardown of any VCN instances that have lost carrier privileges (after a
- // delay)
+ // Schedule teardown of any VCN instances that have lost carrier privileges
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final ParcelUuid subGrp = entry.getKey();
final VcnConfig config = mConfigs.get(subGrp);
final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
- final boolean isValidActiveDataSubIdNotInVcnSubGrp =
- isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
- && !isActiveSubGroup(subGrp, snapshot);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (config == null
@@ -496,31 +487,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- // TODO(b/193687515): Support multiple VCNs active at the same time
- // If directly switching to a subscription not in the current group,
- // teardown immediately to prevent other subscription's network from being
- // outscored by the VCN. Otherwise, teardown after a delay to ensure that
- // SIM profile switches do not trigger the VCN to cycle.
- final long teardownDelayMs =
- isValidActiveDataSubIdNotInVcnSubGrp
- ? 0
- : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- // Guard against case where this is run after a old instance was
- // torn down, and a new instance was started. Verify to ensure
- // correct instance is torn down. This could happen as a result of a
- // Carrier App manually removing/adding a VcnConfig.
- if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
- stopVcnLocked(uuidToTeardown);
-
- // TODO(b/181789060): invoke asynchronously after Vcn notifies
- // through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
- }
- }
- }, instanceToTeardown, teardownDelayMs);
+ stopVcnLocked(uuidToTeardown);
+
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies
+ // through VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d2b4e7a570f..da8c407a501d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -189,7 +189,6 @@ import android.app.ProfilerInfo;
import android.app.PropertyInvalidatedCache;
import android.app.SyncNotedAppOp;
import android.app.WaitResult;
-import android.app.WtfException;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
@@ -12635,7 +12634,6 @@ public class ActivityManagerService extends IActivityManager.Stub
int callingUid;
int callingPid;
boolean instantApp;
- boolean throwWtfException = false;
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLOSP(caller);
@@ -12730,9 +12728,13 @@ public class ActivityManagerService extends IActivityManager.Stub
+ "RECEIVER_NOT_EXPORTED be specified when registering a "
+ "receiver");
} else {
- // will be removed when enforcement is required
+ 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
- throwWtfException = true;
flags |= Context.RECEIVER_EXPORTED;
}
} else if (!requireExplicitFlagForDynamicReceivers) {
@@ -12863,15 +12865,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- if (throwWtfException) {
- throw new WtfException(
- 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");
- }
-
return sticky;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5fc11e8fff7c..8cb20404b3e1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -56,6 +56,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.WakeLockStats;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
@@ -2596,6 +2597,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
/**
+ * Gets a snapshot of wake lock stats
+ * @hide
+ */
+ public WakeLockStats getWakeLockStats() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+ // Wait for the completion of pending works if there is any
+ awaitCompletion();
+ synchronized (mStats) {
+ return mStats.getWakeLockStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2ec4a02a1ff2..eb8a4e9508da 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -349,6 +349,11 @@ public final class BroadcastQueue {
prr.removeCurReceiver(r);
}
}
+
+ // if something bad happens here, launch the app and try again
+ if (app.isKilled()) {
+ throw new RemoteException("app gets killed during broadcasting");
+ }
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index a27e4b77959c..023a11e9ad0f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -238,6 +238,9 @@ public class AudioDeviceInventory {
//------------------------------------------------------------
/*package*/ void dump(PrintWriter pw, String prefix) {
+ pw.println("\n" + prefix + "BECOMING_NOISY_INTENT_DEVICES_SET=");
+ BECOMING_NOISY_INTENT_DEVICES_SET.forEach(device -> {
+ pw.print(" 0x" + Integer.toHexString(device)); });
pw.println("\n" + prefix + "Preferred devices for strategy:");
mPreferredDevices.forEach((strategy, device) -> {
pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); });
@@ -1204,10 +1207,13 @@ public class AudioDeviceInventory {
state == AudioService.CONNECTION_STATE_CONNECTED
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED);
if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
+ Log.i(TAG, "not sending NOISY: state=" + state);
mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
return 0;
}
if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) {
+ Log.i(TAG, "not sending NOISY: device=0x" + Integer.toHexString(device)
+ + " not in set " + BECOMING_NOISY_INTENT_DEVICES_SET);
mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return
return 0;
}
@@ -1217,18 +1223,24 @@ public class AudioDeviceInventory {
if (((di.mDeviceType & AudioSystem.DEVICE_BIT_IN) == 0)
&& BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) {
devices.add(di.mDeviceType);
+ Log.i(TAG, "NOISY: adding 0x" + Integer.toHexString(di.mDeviceType));
}
}
if (musicDevice == AudioSystem.DEVICE_NONE) {
musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
+ Log.i(TAG, "NOISY: musicDevice changing from NONE to 0x"
+ + Integer.toHexString(musicDevice));
}
// always ignore condition on device being actually used for music when in communication
// because music routing is altered in this case.
// also checks whether media routing if affected by a dynamic policy or mirroring
- if (((device == musicDevice) || mDeviceBroker.isInCommunication())
- && AudioSystem.isSingleAudioDeviceType(devices, device)
- && !mDeviceBroker.hasMediaDynamicPolicy()
+ final boolean inCommunication = mDeviceBroker.isInCommunication();
+ final boolean singleAudioDeviceType = AudioSystem.isSingleAudioDeviceType(devices, device);
+ final boolean hasMediaDynamicPolicy = mDeviceBroker.hasMediaDynamicPolicy();
+ if (((device == musicDevice) || inCommunication)
+ && singleAudioDeviceType
+ && !hasMediaDynamicPolicy
&& (musicDevice != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)) {
if (!mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/)
&& !mDeviceBroker.hasAudioFocusUsers()) {
@@ -1241,6 +1253,12 @@ public class AudioDeviceInventory {
}
mDeviceBroker.postBroadcastBecomingNoisy();
delay = AudioService.BECOMING_NOISY_DELAY_MS;
+ } else {
+ Log.i(TAG, "not sending NOISY: device:0x" + Integer.toHexString(device)
+ + " musicDevice:0x" + Integer.toHexString(musicDevice)
+ + " inComm:" + inCommunication
+ + " mediaPolicy:" + hasMediaDynamicPolicy
+ + " singleDevice:" + singleAudioDeviceType);
}
mmi.set(MediaMetrics.Property.DELAY_MS, delay).record();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index b73e91173a43..26bbb403f39f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -48,7 +50,6 @@ public abstract class BaseClientMonitor extends LoggableMonitor
* Interface that ClientMonitor holders should use to receive callbacks.
*/
public interface Callback {
-
/**
* Invoked when the ClientMonitor operation has been started (e.g. reached the head of
* the queue and becomes the current operation).
@@ -203,7 +204,8 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
/** Signals this operation has completed its lifecycle and should no longer be used. */
- void destroy() {
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void destroy() {
mAlreadyDone = true;
if (mToken != null) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index a358bc2bad55..1f91c4d6803e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -17,15 +17,14 @@
package com.android.server.biometrics.sensors;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
@@ -55,6 +54,7 @@ import java.util.Locale;
* We currently assume (and require) that each biometric sensor have its own instance of a
* {@link BiometricScheduler}. See {@link CoexCoordinator}.
*/
+@MainThread
public class BiometricScheduler {
private static final String BASE_TAG = "BiometricScheduler";
@@ -110,123 +110,6 @@ public class BiometricScheduler {
}
}
- /**
- * Contains all the necessary information for a HAL operation.
- */
- @VisibleForTesting
- static final class Operation {
-
- /**
- * The operation is added to the list of pending operations and waiting for its turn.
- */
- static final int STATE_WAITING_IN_QUEUE = 0;
-
- /**
- * The operation is added to the list of pending operations, but a subsequent operation
- * has been added. This state only applies to {@link Interruptable} operations. When this
- * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
- */
- static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
-
- /**
- * The operation has reached the front of the queue and has started.
- */
- static final int STATE_STARTED = 2;
-
- /**
- * The operation was started, but is now canceling. Operations should wait for the HAL to
- * acknowledge that the operation was canceled, at which point it finishes.
- */
- static final int STATE_STARTED_CANCELING = 3;
-
- /**
- * The operation has reached the head of the queue but is waiting for BiometricService
- * to acknowledge and start the operation.
- */
- static final int STATE_WAITING_FOR_COOKIE = 4;
-
- /**
- * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
- */
- static final int STATE_FINISHED = 5;
-
- @IntDef({STATE_WAITING_IN_QUEUE,
- STATE_WAITING_IN_QUEUE_CANCELING,
- STATE_STARTED,
- STATE_STARTED_CANCELING,
- STATE_WAITING_FOR_COOKIE,
- STATE_FINISHED})
- @Retention(RetentionPolicy.SOURCE)
- @interface OperationState {}
-
- @NonNull final BaseClientMonitor mClientMonitor;
- @Nullable final BaseClientMonitor.Callback mClientCallback;
- @OperationState int mState;
-
- Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback
- ) {
- this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
- }
-
- protected Operation(
- @NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback,
- @OperationState int state
- ) {
- mClientMonitor = clientMonitor;
- mClientCallback = callback;
- mState = state;
- }
-
- public boolean isHalOperation() {
- return mClientMonitor instanceof HalClientMonitor<?>;
- }
-
- /**
- * @return true if the operation requires the HAL, and the HAL is null.
- */
- public boolean isUnstartableHalOperation() {
- if (isHalOperation()) {
- final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
- if (client.getFreshDaemon() == null) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- return mClientMonitor + ", State: " + mState;
- }
- }
-
- /**
- * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will
- * kill the current operation and forcibly start the next.
- */
- private static final class CancellationWatchdog implements Runnable {
- static final int DELAY_MS = 3000;
-
- final String tag;
- final Operation operation;
- CancellationWatchdog(String tag, Operation operation) {
- this.tag = tag;
- this.operation = operation;
- }
-
- @Override
- public void run() {
- if (operation.mState != Operation.STATE_FINISHED) {
- Slog.e(tag, "[Watchdog Triggered]: " + operation);
- operation.mClientMonitor.mCallback
- .onClientFinished(operation.mClientMonitor, false /* success */);
- }
- }
- }
-
private static final class CrashState {
static final int NUM_ENTRIES = 10;
final String timestamp;
@@ -263,10 +146,9 @@ public class BiometricScheduler {
private final @SensorType int mSensorType;
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService;
- @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
- @NonNull private final InternalCallback mInternalCallback;
- @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations;
- @VisibleForTesting @Nullable Operation mCurrentOperation;
+ @NonNull protected final Handler mHandler;
+ @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations;
+ @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation;
@NonNull private final ArrayDeque<CrashState> mCrashStates;
private int mTotalOperationsHandled;
@@ -277,7 +159,7 @@ public class BiometricScheduler {
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- public class InternalCallback implements BaseClientMonitor.Callback {
+ private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -286,16 +168,11 @@ public class BiometricScheduler {
mCoexCoordinator.addAuthenticationClient(mSensorType,
(AuthenticationClient<?>) clientMonitor);
}
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
- }
}
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
mHandler.post(() -> {
- clientMonitor.destroy();
if (mCurrentOperation == null) {
Slog.e(getTag(), "[Finishing] " + clientMonitor
+ " but current operation is null, success: " + success
@@ -303,9 +180,9 @@ public class BiometricScheduler {
return;
}
- if (clientMonitor != mCurrentOperation.mClientMonitor) {
+ if (!mCurrentOperation.isFor(clientMonitor)) {
Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match"
- + " current: " + mCurrentOperation.mClientMonitor);
+ + " current: " + mCurrentOperation);
return;
}
@@ -315,36 +192,33 @@ public class BiometricScheduler {
(AuthenticationClient<?>) clientMonitor);
}
- mCurrentOperation.mState = Operation.STATE_FINISHED;
-
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success);
- }
-
if (mGestureAvailabilityDispatcher != null) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(), false /* active */);
+ mCurrentOperation.getSensorId(), false /* active */);
}
if (mRecentOperations.size() >= mRecentOperationsLimit) {
mRecentOperations.remove(0);
}
- mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum());
+ mRecentOperations.add(mCurrentOperation.getProtoEnum());
mCurrentOperation = null;
mTotalOperationsHandled++;
startNextOperationIfIdle();
});
}
- }
+ };
@VisibleForTesting
- BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ BiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull IBiometricService biometricService, int recentOperationsLimit,
+ @NonNull IBiometricService biometricService,
+ int recentOperationsLimit,
@NonNull CoexCoordinator coexCoordinator) {
mBiometricTag = tag;
+ mHandler = handler;
mSensorType = sensorType;
- mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>();
mBiometricService = biometricService;
@@ -356,24 +230,26 @@ public class BiometricScheduler {
/**
* Creates a new scheduler.
+ *
* @param tag for the specific instance of the scheduler. Should be unique.
+ * @param handler handler for callbacks (all methods of this class must be called on the
+ * thread associated with this handler)
* @param sensorType the sensorType that this scheduler is handling.
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
* (such as fingerprint swipe).
*/
public BiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
@SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
- CoexCoordinator.getInstance());
+ this(tag, handler, sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance());
}
- /**
- * @return A reference to the internal callback that should be invoked whenever the scheduler
- * needs to (e.g. client started, client finished).
- */
- @NonNull protected InternalCallback getInternalCallback() {
+ @VisibleForTesting
+ public BaseClientMonitor.Callback getInternalCallback() {
return mInternalCallback;
}
@@ -392,72 +268,46 @@ public class BiometricScheduler {
}
mCurrentOperation = mPendingOperations.poll();
- final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor;
Slog.d(getTag(), "[Polled] " + mCurrentOperation);
// If the operation at the front of the queue has been marked for cancellation, send
// ERROR_CANCELED. No need to start this client.
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
+ if (mCurrentOperation.isMarkedCanceling()) {
Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation);
- if (!(currentClient instanceof Interruptable)) {
- throw new IllegalStateException("Mis-implemented client or scheduler, "
- + "trying to cancel non-interruptable operation: " + mCurrentOperation);
- }
-
- final Interruptable interruptable = (Interruptable) currentClient;
- interruptable.cancelWithoutStarting(getInternalCallback());
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
// Now we wait for the client to send its FinishCallback, which kicks off the next
// operation.
return;
}
- if (mGestureAvailabilityDispatcher != null
- && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) {
+ if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) {
mGestureAvailabilityDispatcher.markSensorActive(
- mCurrentOperation.mClientMonitor.getSensorId(),
- true /* active */);
+ mCurrentOperation.getSensorId(), true /* active */);
}
// Not all operations start immediately. BiometricPrompt waits for its operation
// to arrive at the head of the queue, before pinging it to start.
- final boolean shouldStartNow = currentClient.getCookie() == 0;
- if (shouldStartNow) {
- if (mCurrentOperation.isUnstartableHalOperation()) {
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
+ final int cookie = mCurrentOperation.isReadyToStart();
+ if (cookie == 0) {
+ if (!mCurrentOperation.start(mInternalCallback)) {
// Note down current length of queue
final int pendingOperationsLength = mPendingOperations.size();
- final Operation lastOperation = mPendingOperations.peekLast();
+ final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast();
Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation
+ ". Last pending operation: " + lastOperation);
- // For current operations, 1) unableToStart, which notifies the caller-side, then
- // 2) notify operation's callback, to notify applicable system service that the
- // operation failed.
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(
- mCurrentOperation.mClientMonitor, false /* success */);
- }
-
// Then for each operation currently in the pending queue at the time of this
// failure, do the same as above. Otherwise, it's possible that something like
// setActiveUser fails, but then authenticate (for the wrong user) is invoked.
for (int i = 0; i < pendingOperationsLength; i++) {
- final Operation operation = mPendingOperations.pollFirst();
- if (operation == null) {
+ final BiometricSchedulerOperation operation = mPendingOperations.pollFirst();
+ if (operation != null) {
+ Slog.w(getTag(), "[Aborting Operation] " + operation);
+ operation.abort();
+ } else {
Slog.e(getTag(), "Null operation, index: " + i
+ ", expected length: " + pendingOperationsLength);
- break;
- }
- if (operation.isHalOperation()) {
- ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart();
- }
- if (operation.mClientCallback != null) {
- operation.mClientCallback.onClientFinished(operation.mClientMonitor,
- false /* success */);
}
- Slog.w(getTag(), "[Aborted Operation] " + operation);
}
// It's possible that during cleanup a new set of operations came in. We can try to
@@ -465,25 +315,20 @@ public class BiometricScheduler {
// actually be multiple operations (i.e. updateActiveUser + authenticate).
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] " + mCurrentOperation);
- currentClient.start(getInternalCallback());
- mCurrentOperation.mState = Operation.STATE_STARTED;
}
} else {
try {
- mBiometricService.onReadyForAuthentication(currentClient.getCookie());
+ mBiometricService.onReadyForAuthentication(cookie);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when contacting BiometricService", e);
}
Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE;
}
}
/**
* Starts the {@link #mCurrentOperation} if
- * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
+ * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and
* 2) its cookie matches this cookie
*
* This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
@@ -499,45 +344,13 @@ public class BiometricScheduler {
Slog.e(getTag(), "Current operation is null");
return;
}
- if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) {
- if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: "
- + mCurrentOperation);
- // This should trigger the internal onClientFinished callback, which clears the
- // operation and starts the next one.
- final ErrorConsumer errorConsumer =
- (ErrorConsumer) mCurrentOperation.mClientMonitor;
- errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- 0 /* vendorCode */);
- return;
- } else {
- Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
- + ", expected STATE_WAITING_FOR_COOKIE");
- return;
- }
- }
- if (mCurrentOperation.mClientMonitor.getCookie() != cookie) {
- Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation
- + ", received: " + cookie);
- return;
- }
- if (mCurrentOperation.isUnstartableHalOperation()) {
+ if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) {
+ Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation);
+ } else {
Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation);
- // This is BiometricPrompt trying to auth but something's wrong with the HAL.
- final HalClientMonitor<?> halClientMonitor =
- (HalClientMonitor<?>) mCurrentOperation.mClientMonitor;
- halClientMonitor.unableToStart();
- if (mCurrentOperation.mClientCallback != null) {
- mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor,
- false /* success */);
- }
mCurrentOperation = null;
startNextOperationIfIdle();
- } else {
- Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation);
- mCurrentOperation.mState = Operation.STATE_STARTED;
- mCurrentOperation.mClientMonitor.start(getInternalCallback());
}
}
@@ -562,17 +375,13 @@ public class BiometricScheduler {
// pending clients as canceling. Once they reach the head of the queue, the scheduler will
// send ERROR_CANCELED and skip the operation.
if (clientMonitor.interruptsPrecedingClients()) {
- for (Operation operation : mPendingOperations) {
- if (operation.mClientMonitor instanceof Interruptable
- && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) {
- Slog.d(getTag(), "New client incoming, marking pending client as canceling: "
- + operation.mClientMonitor);
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
- }
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ Slog.d(getTag(), "New client, marking pending op as canceling: " + operation);
+ operation.markCanceling();
}
}
- mPendingOperations.add(new Operation(clientMonitor, clientCallback));
+ mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback));
Slog.d(getTag(), "[Added] " + clientMonitor
+ ", new queue size: " + mPendingOperations.size());
@@ -580,67 +389,34 @@ public class BiometricScheduler {
// cancellable, start the cancellation process.
if (clientMonitor.interruptsPrecedingClients()
&& mCurrentOperation != null
- && mCurrentOperation.mClientMonitor instanceof Interruptable
- && mCurrentOperation.mState == Operation.STATE_STARTED) {
+ && mCurrentOperation.isInterruptable()
+ && mCurrentOperation.isStarted()) {
Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
- }
-
- startNextOperationIfIdle();
- }
-
- private void cancelInternal(Operation operation) {
- if (operation != mCurrentOperation) {
- Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation);
- return;
- }
- if (!(operation.mClientMonitor instanceof Interruptable)) {
- Slog.w(getTag(), "Operation not interruptable: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_STARTED_CANCELING) {
- Slog.w(getTag(), "Cancel already invoked for operation: " + operation);
- return;
- }
- if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
- Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
- // We can set it to null immediately, since the HAL was never notified to start.
- if (mCurrentOperation != null) {
- mCurrentOperation.mClientMonitor.destroy();
- }
- mCurrentOperation = null;
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
startNextOperationIfIdle();
- return;
}
- Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor);
- final Interruptable interruptable = (Interruptable) operation.mClientMonitor;
- interruptable.cancel();
- operation.mState = Operation.STATE_STARTED_CANCELING;
-
- // Add a watchdog. If the HAL does not acknowledge within the timeout, we will
- // forcibly finish this client.
- mHandler.postDelayed(new CancellationWatchdog(getTag(), operation),
- CancellationWatchdog.DELAY_MS);
}
/**
* Requests to cancel enrollment.
* @param token from the caller, should match the token passed in when requesting enrollment
*/
- public void cancelEnrollment(IBinder token) {
- if (mCurrentOperation == null) {
- Slog.e(getTag(), "Unable to cancel enrollment, null operation");
- return;
- }
- final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient;
- final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token;
- if (!isEnrolling || !tokenMatches) {
- Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling
- + " tokenMatches: " + tokenMatches);
- return;
- }
+ public void cancelEnrollment(IBinder token, long requestId) {
+ Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId);
- cancelInternal(mCurrentOperation);
+ if (mCurrentOperation != null
+ && canCancelEnrollOperation(mCurrentOperation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
+ } else {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
+ if (canCancelEnrollOperation(operation, token, requestId)) {
+ Slog.d(getTag(), "Cancelling pending enrollment op: " + operation);
+ operation.markCanceling();
+ }
+ }
+ }
}
/**
@@ -649,62 +425,42 @@ public class BiometricScheduler {
* @param requestId the id returned when requesting authentication
*/
public void cancelAuthenticationOrDetection(IBinder token, long requestId) {
- Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId
- + " current: " + mCurrentOperation
- + " stack size: " + mPendingOperations.size());
+ Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId);
if (mCurrentOperation != null
&& canCancelAuthOperation(mCurrentOperation, token, requestId)) {
- Slog.d(getTag(), "Cancelling: " + mCurrentOperation);
- cancelInternal(mCurrentOperation);
+ Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation);
+ mCurrentOperation.cancel(mHandler, mInternalCallback);
} else {
- // Look through the current queue for all authentication clients for the specified
- // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking
- // all of them, instead of just the first one, since the API surface currently doesn't
- // allow us to distinguish between multiple authentication requests from the same
- // process. However, this generally does not happen anyway, and would be a class of
- // bugs on its own.
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
if (canCancelAuthOperation(operation, token, requestId)) {
- Slog.d(getTag(), "Marking " + operation
- + " as STATE_WAITING_IN_QUEUE_CANCELING");
- operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING;
+ Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation);
+ operation.markCanceling();
}
}
}
}
- private static boolean canCancelAuthOperation(Operation operation, IBinder token,
- long requestId) {
- // TODO: restrict callers that can cancel without requestId (negative value)?
- return isAuthenticationOrDetectionOperation(operation)
- && operation.mClientMonitor.getToken() == token
- && isMatchingRequestId(operation, requestId);
- }
-
- // By default, monitors are not associated with a request id to retain the original
- // behavior (i.e. if no requestId is explicitly set then assume it matches)
- private static boolean isMatchingRequestId(Operation operation, long requestId) {
- return !operation.mClientMonitor.hasRequestId()
- || operation.mClientMonitor.getRequestId() == requestId;
+ private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
+ return operation.isEnrollOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
}
- private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) {
- final boolean isAuthentication =
- operation.mClientMonitor instanceof AuthenticationConsumer;
- final boolean isDetection =
- operation.mClientMonitor instanceof DetectionConsumer;
- return isAuthentication || isDetection;
+ private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation,
+ IBinder token, long requestId) {
+ // TODO: restrict callers that can cancel without requestId (negative value)?
+ return operation.isAuthenticationOrDetectionOperation()
+ && operation.isMatchingToken(token)
+ && operation.isMatchingRequestId(requestId);
}
/**
* @return the current operation
*/
public BaseClientMonitor getCurrentClient() {
- if (mCurrentOperation == null) {
- return null;
- }
- return mCurrentOperation.mClientMonitor;
+ return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null;
}
public int getCurrentPendingCount() {
@@ -719,7 +475,7 @@ public class BiometricScheduler {
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
final String timestamp = dateFormat.format(new Date(System.currentTimeMillis()));
final List<String> pendingOperations = new ArrayList<>();
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pendingOperations.add(operation.toString());
}
@@ -735,7 +491,7 @@ public class BiometricScheduler {
pw.println("Type: " + mSensorType);
pw.println("Current operation: " + mCurrentOperation);
pw.println("Pending operations: " + mPendingOperations.size());
- for (Operation operation : mPendingOperations) {
+ for (BiometricSchedulerOperation operation : mPendingOperations) {
pw.println("Pending operation: " + operation);
}
for (CrashState crashState : mCrashStates) {
@@ -746,7 +502,7 @@ public class BiometricScheduler {
public byte[] dumpProtoState(boolean clearSchedulerBuffer) {
final ProtoOutputStream proto = new ProtoOutputStream();
proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null
- ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE);
+ ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE);
proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled);
if (!mRecentOperations.isEmpty()) {
@@ -771,6 +527,7 @@ public class BiometricScheduler {
* HAL dies.
*/
public void reset() {
+ Slog.d(getTag(), "Resetting scheduler");
mPendingOperations.clear();
mCurrentOperation = null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
new file mode 100644
index 000000000000..a8cce153dc70
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the necessary information for a HAL operation.
+ */
+public class BiometricSchedulerOperation {
+ protected static final String TAG = "BiometricSchedulerOperation";
+
+ /**
+ * The operation is added to the list of pending operations and waiting for its turn.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE = 0;
+
+ /**
+ * The operation is added to the list of pending operations, but a subsequent operation
+ * has been added. This state only applies to {@link Interruptable} operations. When this
+ * operation reaches the head of the queue, it will send ERROR_CANCELED and finish.
+ */
+ protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1;
+
+ /**
+ * The operation has reached the front of the queue and has started.
+ */
+ protected static final int STATE_STARTED = 2;
+
+ /**
+ * The operation was started, but is now canceling. Operations should wait for the HAL to
+ * acknowledge that the operation was canceled, at which point it finishes.
+ */
+ protected static final int STATE_STARTED_CANCELING = 3;
+
+ /**
+ * The operation has reached the head of the queue but is waiting for BiometricService
+ * to acknowledge and start the operation.
+ */
+ protected static final int STATE_WAITING_FOR_COOKIE = 4;
+
+ /**
+ * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+ */
+ protected static final int STATE_FINISHED = 5;
+
+ @IntDef({STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_IN_QUEUE_CANCELING,
+ STATE_STARTED,
+ STATE_STARTED_CANCELING,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_FINISHED})
+ @Retention(RetentionPolicy.SOURCE)
+ protected @interface OperationState {}
+
+ private static final int CANCEL_WATCHDOG_DELAY_MS = 3000;
+
+ @NonNull
+ private final BaseClientMonitor mClientMonitor;
+ @Nullable
+ private final BaseClientMonitor.Callback mClientCallback;
+ @OperationState
+ private int mState;
+ @VisibleForTesting
+ @NonNull
+ final Runnable mCancelWatchdog;
+
+ BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback
+ ) {
+ this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
+ }
+
+ protected BiometricSchedulerOperation(
+ @NonNull BaseClientMonitor clientMonitor,
+ @Nullable BaseClientMonitor.Callback callback,
+ @OperationState int state
+ ) {
+ mClientMonitor = clientMonitor;
+ mClientCallback = callback;
+ mState = state;
+ mCancelWatchdog = () -> {
+ if (!isFinished()) {
+ Slog.e(TAG, "[Watchdog Triggered]: " + this);
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+ }
+ };
+ }
+
+ /**
+ * Zero if this operation is ready to start or has already started. A non-zero cookie
+ * is returned if the operation has not started and is waiting on
+ * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}.
+ *
+ * @return cookie or 0 if ready/started
+ */
+ public int isReadyToStart() {
+ if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) {
+ final int cookie = mClientMonitor.getCookie();
+ if (cookie != 0) {
+ mState = STATE_WAITING_FOR_COOKIE;
+ }
+ return cookie;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Start this operation without waiting for a cookie
+ * (i.e. {@link #isReadyToStart() returns zero}
+ *
+ * @param callback lifecycle callback
+ * @return if this operation started
+ */
+ public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != 0) {
+ throw new IllegalStateException("operation requires cookie");
+ }
+
+ return doStart(callback);
+ }
+
+ /**
+ * Start this operation after receiving the given cookie.
+ *
+ * @param callback lifecycle callback
+ * @param cookie cookie indicting the operation should begin
+ * @return if this operation started
+ */
+ public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+ checkInState("start",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (mClientMonitor.getCookie() != cookie) {
+ Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie);
+ return false;
+ }
+
+ return doStart(callback);
+ }
+
+ private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+
+ if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
+ Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
+
+ cb.onClientFinished(mClientMonitor, true /* success */);
+ if (mClientMonitor instanceof ErrorConsumer) {
+ final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor;
+ errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ } else {
+ Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer");
+ }
+
+ return false;
+ }
+
+ if (isUnstartableHalOperation()) {
+ Slog.v(TAG, "unable to start: " + this);
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ cb.onClientFinished(mClientMonitor, false /* success */);
+ return false;
+ }
+
+ mState = STATE_STARTED;
+ mClientMonitor.start(cb);
+
+ Slog.v(TAG, "started: " + this);
+ return true;
+ }
+
+ /**
+ * Abort a pending operation.
+ *
+ * This is similar to cancel but the operation must not have been started. It will
+ * immediately abort the operation and notify the client that it has finished unsuccessfully.
+ */
+ public void abort() {
+ checkInState("cannot abort a non-pending operation",
+ STATE_WAITING_IN_QUEUE,
+ STATE_WAITING_FOR_COOKIE,
+ STATE_WAITING_IN_QUEUE_CANCELING);
+
+ if (isHalOperation()) {
+ ((HalClientMonitor<?>) mClientMonitor).unableToStart();
+ }
+ getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+
+ Slog.v(TAG, "Aborted: " + this);
+ }
+
+ /** Flags this operation as canceled, but does not cancel it until started. */
+ public void markCanceling() {
+ if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) {
+ mState = STATE_WAITING_IN_QUEUE_CANCELING;
+ Slog.v(TAG, "Marked cancelling: " + this);
+ }
+ }
+
+ /**
+ * Cancel the operation now.
+ *
+ * @param handler handler to use for the cancellation watchdog
+ * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
+ * the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+ */
+ public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+ checkNotInState("cancel", STATE_FINISHED);
+
+ final int currentState = mState;
+ if (!isInterruptable()) {
+ Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this);
+ return;
+ }
+ if (currentState == STATE_STARTED_CANCELING) {
+ Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this);
+ return;
+ }
+
+ mState = STATE_STARTED_CANCELING;
+ if (currentState == STATE_WAITING_IN_QUEUE
+ || currentState == STATE_WAITING_IN_QUEUE_CANCELING
+ || currentState == STATE_WAITING_FOR_COOKIE) {
+ Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback));
+ } else {
+ Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor);
+ ((Interruptable) mClientMonitor).cancel();
+ }
+
+ // forcibly finish this client if the HAL does not acknowledge within the timeout
+ handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback() {
+ return getWrappedCallback(null);
+ }
+
+ @NonNull
+ private BaseClientMonitor.Callback getWrappedCallback(
+ @Nullable BaseClientMonitor.Callback callback) {
+ final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mClientMonitor.destroy();
+ mState = STATE_FINISHED;
+ }
+ };
+ return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+ }
+
+ /** {@link BaseClientMonitor#getSensorId()}. */
+ public int getSensorId() {
+ return mClientMonitor.getSensorId();
+ }
+
+ /** {@link BaseClientMonitor#getProtoEnum()}. */
+ public int getProtoEnum() {
+ return mClientMonitor.getProtoEnum();
+ }
+
+ /** {@link BaseClientMonitor#getTargetUserId()}. */
+ public int getTargetUserId() {
+ return mClientMonitor.getTargetUserId();
+ }
+
+ /** If the given clientMonitor is the same as the one in the constructor. */
+ public boolean isFor(@NonNull BaseClientMonitor clientMonitor) {
+ return mClientMonitor == clientMonitor;
+ }
+
+ /** If this operation is {@link Interruptable}. */
+ public boolean isInterruptable() {
+ return mClientMonitor instanceof Interruptable;
+ }
+
+ private boolean isHalOperation() {
+ return mClientMonitor instanceof HalClientMonitor<?>;
+ }
+
+ private boolean isUnstartableHalOperation() {
+ if (isHalOperation()) {
+ final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor;
+ if (client.getFreshDaemon() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** If this operation is an enrollment. */
+ public boolean isEnrollOperation() {
+ return mClientMonitor instanceof EnrollClient;
+ }
+
+ /** If this operation is authentication. */
+ public boolean isAuthenticateOperation() {
+ return mClientMonitor instanceof AuthenticationClient;
+ }
+
+ /** If this operation is authentication or detection. */
+ public boolean isAuthenticationOrDetectionOperation() {
+ final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer;
+ final boolean isDetection = mClientMonitor instanceof DetectionConsumer;
+ return isAuthentication || isDetection;
+ }
+
+ /** If this operation performs acquisition {@link AcquisitionClient}. */
+ public boolean isAcquisitionOperation() {
+ return mClientMonitor instanceof AcquisitionClient;
+ }
+
+ /**
+ * If this operation matches the original requestId.
+ *
+ * By default, monitors are not associated with a request id to retain the original
+ * behavior (i.e. if no requestId is explicitly set then assume it matches)
+ *
+ * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}.
+ */
+ public boolean isMatchingRequestId(long requestId) {
+ return !mClientMonitor.hasRequestId()
+ || mClientMonitor.getRequestId() == requestId;
+ }
+
+ /** If the token matches */
+ public boolean isMatchingToken(@Nullable IBinder token) {
+ return mClientMonitor.getToken() == token;
+ }
+
+ /** If this operation has started. */
+ public boolean isStarted() {
+ return mState == STATE_STARTED;
+ }
+
+ /** If this operation is cancelling but has not yet completed. */
+ public boolean isCanceling() {
+ return mState == STATE_STARTED_CANCELING;
+ }
+
+ /** If this operation has finished and completed its lifecycle. */
+ public boolean isFinished() {
+ return mState == STATE_FINISHED;
+ }
+
+ /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */
+ public boolean isMarkedCanceling() {
+ return mState == STATE_WAITING_IN_QUEUE_CANCELING;
+ }
+
+ /**
+ * The monitor passed to the constructor.
+ * @deprecated avoid using and move to encapsulate within the operation
+ */
+ @Deprecated
+ public BaseClientMonitor getClientMonitor() {
+ return mClientMonitor;
+ }
+
+ private void checkNotInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ throw new IllegalStateException(message + ": illegal state= " + state);
+ }
+ }
+ }
+
+ private void checkInState(String message, @OperationState int... states) {
+ for (int state : states) {
+ if (mState == state) {
+ return;
+ }
+ }
+ throw new IllegalStateException(message + ": illegal state= " + mState);
+ }
+
+ @Override
+ public String toString() {
+ return mClientMonitor + ", State: " + mState;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index fab98b6581a3..d5093c756415 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -32,6 +32,11 @@ public interface Interruptable {
* {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
+ *
+ * This method must invoke
+ * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+ * given callback (with success).
+ *
* @param callback invoked when the operation is completed.
*/
void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index b056bf897b5c..19eaa178c7c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -16,10 +16,13 @@
package com.android.server.biometrics.sensors;
+import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
+import android.os.Handler;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
@@ -68,9 +71,8 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
return;
}
- Slog.d(getTag(), "[Client finished] "
- + clientMonitor + ", success: " + success);
- if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) {
+ Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success);
+ if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) {
mCurrentOperation = null;
startNextOperationIfIdle();
} else {
@@ -83,26 +85,31 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
}
@VisibleForTesting
- UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ UserAwareBiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback,
@NonNull CoexCoordinator coexCoordinator) {
- super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
+ super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService,
LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
mCurrentUserRetriever = currentUserRetriever;
mUserSwitchCallback = userSwitchCallback;
}
- public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
+ public UserAwareBiometricScheduler(@NonNull String tag,
+ @NonNull Handler handler,
+ @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback) {
- this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
- userSwitchCallback, CoexCoordinator.getInstance());
+ this(tag, handler, sensorType, gestureAvailabilityDispatcher,
+ IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance());
}
@Override
@@ -122,7 +129,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
}
final int currentUserId = mCurrentUserRetriever.getCurrentUserId();
- final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId();
+ final int nextUserId = mPendingOperations.getFirst().getTargetUserId();
if (nextUserId == currentUserId) {
super.startNextOperationIfIdle();
@@ -133,8 +140,8 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
new ClientFinishedCallback(startClient);
Slog.d(getTag(), "[Starting User] " + startClient);
- mCurrentOperation = new Operation(
- startClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ startClient, finishedCallback, STATE_STARTED);
startClient.start(finishedCallback);
} else {
if (mStopUserClient != null) {
@@ -147,8 +154,8 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
Slog.d(getTag(), "[Stopping User] current: " + currentUserId
+ ", next: " + nextUserId + ". " + mStopUserClient);
- mCurrentOperation = new Operation(
- mStopUserClient, finishedCallback, Operation.STATE_STARTED);
+ mCurrentOperation = new BiometricSchedulerOperation(
+ mStopUserClient, finishedCallback, STATE_STARTED);
mStopUserClient.start(finishedCallback);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 675ee545a14f..039b08e805c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -213,7 +213,7 @@ public class FaceService extends SystemService {
}
@Override // Binder call
- public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
@@ -221,23 +221,24 @@ public class FaceService extends SystemService {
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
}
@Override // Binder call
- public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
+ public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
// TODO(b/145027036): Implement this.
+ return -1;
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -246,7 +247,7 @@ public class FaceService extends SystemService {
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@Override // Binder call
@@ -624,7 +625,7 @@ public class FaceService extends SystemService {
private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
mServiceProviders.add(
- new Face10(getContext(), hidlSensor, mLockoutResetDispatcher));
+ Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index e099ba372b05..77e431c81192 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -94,12 +94,12 @@ public interface ServiceProvider {
void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge);
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
boolean debugConsent);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index a806277ed45e..aae4fbe9b0d7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -82,13 +82,14 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
+ @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser,
boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 4bae7756abe0..ae507abea537 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -327,17 +327,18 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(
sensorId).getSensorProperties().maxEnrollmentsPerUser;
final FaceEnrollClient client = new FaceEnrollClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
debugConsent);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@@ -351,11 +352,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 206b8f0779e8..39270430c21d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -494,7 +494,7 @@ public class Sensor {
mToken = new Binder();
mHandler = handler;
mSensorProperties = sensorProperties;
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mScheduler = new UserAwareBiometricScheduler(tag, mHandler,
BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f4dcbbba21d7..493c0a05e379 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -333,12 +333,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
Face10(@NonNull Context context,
@NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
mSensorProperties = sensorProps;
mContext = context;
mSensorId = sensorProps.sensorId;
mScheduler = scheduler;
- mHandler = new Handler(Looper.getMainLooper());
+ mHandler = handler;
mUsageStats = new UsageStats(context);
mAuthenticatorIds = new HashMap<>();
mLazyDaemon = Face10.this::getDaemon;
@@ -357,10 +358,12 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
}
- public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
+ public static Face10 newInstance(@NonNull Context context,
+ @NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
- this(context, sensorProps, lockoutResetDispatcher,
- new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
+ final Handler handler = new Handler(Looper.getMainLooper());
+ return new Face10(context, sensorProps, lockoutResetDispatcher, handler,
+ new BiometricScheduler(TAG, handler, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityTracker */));
}
@@ -573,10 +576,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
@Nullable Surface previewSurface, boolean debugConsent) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -584,7 +588,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -598,13 +602,12 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 80828cced4e8..31e5c86103fb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -53,12 +53,13 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
+ setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 3e70ee52ff1b..6366e19ef191 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -249,7 +249,7 @@ public class FingerprintService extends SystemService {
}
@Override // Binder call
- public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
+ public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -257,15 +257,15 @@ public class FingerprintService extends SystemService {
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
- return;
+ return -1;
}
- provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, enrollReason);
}
@Override // Binder call
- public void cancelEnrollment(final IBinder token) {
+ public void cancelEnrollment(final IBinder token, long requestId) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -274,7 +274,7 @@ public class FingerprintService extends SystemService {
return;
}
- provider.second.cancelEnrollment(provider.first, token);
+ provider.second.cancelEnrollment(provider.first, token, requestId);
}
@SuppressWarnings("deprecation")
@@ -818,7 +818,7 @@ public class FingerprintService extends SystemService {
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
- mFingerprintStateCallback, hidlSensor,
+ mFingerprintStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
mServiceProviders.add(fingerprint21);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1772f814dd10..535705c63cab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -88,11 +88,11 @@ public interface ServiceProvider {
/**
* Schedules fingerprint enrollment.
*/
- void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
+ long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
@NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
- void cancelEnrollment(int sensorId, @NonNull IBinder token);
+ void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index ccb34aad3198..67507ccbbbfe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -57,7 +57,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
private boolean mIsPointerDown;
FingerprintEnrollClient(@NonNull Context context,
- @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@@ -69,6 +69,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
!sensorProps.isAnyUdfpsType() /* shouldVibrate */);
+ setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 734b1737dfbc..eb16c763dea6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -347,15 +347,16 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
.maxEnrollmentsPerUser;
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mSensors.get(sensorId).getLazySession(), token,
+ mSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getSensorProperties(),
@@ -378,11 +379,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() ->
+ mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 59e4b582ca84..256761a61a72 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -449,7 +449,7 @@ class Sensor {
mHandler = handler;
mSensorProperties = sensorProperties;
mLockoutCache = new LockoutCache();
- mScheduler = new UserAwareBiometricScheduler(tag,
+ mScheduler = new UserAwareBiometricScheduler(tag, handler,
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 5f2f4cf6ef3c..d352cda609e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -42,7 +42,6 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -320,7 +319,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
Fingerprint21(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
+ @NonNull BiometricScheduler scheduler,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
@@ -356,16 +356,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
public static Fingerprint21 newInstance(@NonNull Context context,
@NonNull FingerprintStateCallback fingerprintStateCallback,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
+ @NonNull Handler handler,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
- new BiometricScheduler(TAG,
+ new BiometricScheduler(TAG, handler,
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId,
- context, handler,
- scheduler);
+ context, handler, scheduler);
return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
lockoutResetDispatcher, controller);
}
@@ -558,18 +557,20 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
- public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@FingerprintManager.EnrollReason int enrollReason) {
+ final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
- mSidefpsController, enrollReason);
+ mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+ mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
+ enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -588,13 +589,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
});
});
+ return id;
}
@Override
- public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelEnrollment(token);
- });
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
+ mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index dd68b4d37e2a..20dab5552df9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,7 +26,6 @@ import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.FingerprintStateListener;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
import android.os.IBinder;
@@ -135,43 +134,17 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
@NonNull private final RestartAuthRunnable mRestartAuthRunnable;
private static class TestableBiometricScheduler extends BiometricScheduler {
- @NonNull private final TestableInternalCallback mInternalCallback;
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
- TestableBiometricScheduler(@NonNull String tag,
+ TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
- super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
+ super(tag, handler, BiometricScheduler.SENSOR_TYPE_FP_OTHER,
gestureAvailabilityDispatcher);
- mInternalCallback = new TestableInternalCallback();
- }
-
- class TestableInternalCallback extends InternalCallback {
- @Override
- public void onClientStarted(BaseClientMonitor clientMonitor) {
- super.onClientStarted(clientMonitor);
- Slog.d(TAG, "Client started: " + clientMonitor);
- mFingerprint21.setDebugMessage("Started: " + clientMonitor);
- }
-
- @Override
- public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) {
- super.onClientFinished(clientMonitor, success);
- Slog.d(TAG, "Client finished: " + clientMonitor);
- mFingerprint21.setDebugMessage("Finished: " + clientMonitor);
- }
}
void init(@NonNull Fingerprint21UdfpsMock fingerprint21) {
mFingerprint21 = fingerprint21;
}
-
- /**
- * Expose the internal finish callback so it can be used for testing
- */
- @Override
- @NonNull protected InternalCallback getInternalCallback() {
- return mInternalCallback;
- }
}
/**
@@ -280,7 +253,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final Handler handler = new Handler(Looper.getMainLooper());
final TestableBiometricScheduler scheduler =
- new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
+ new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1ebf44ca707f..cc50bdfb59ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -55,7 +55,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, int userId,
+ long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@@ -64,6 +64,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
+ setRequestId(requestId);
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mEnrollReason = enrollReason;
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 72e900bbad18..cc9efbc64c02 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -68,7 +68,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsManagerInternal;
import java.time.Clock;
import java.time.ZoneId;
@@ -262,8 +261,10 @@ public class MultipathPolicyTracker {
private long getNetworkTotalBytes(long start, long end) {
try {
- return LocalServices.getService(NetworkStatsManagerInternal.class)
- .getNetworkTotalBytes(mNetworkTemplate, start, end);
+ final android.app.usage.NetworkStats.Bucket ret =
+ mContext.getSystemService(NetworkStatsManager.class)
+ .querySummaryForDevice(mNetworkTemplate, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
} catch (RuntimeException e) {
Log.w(TAG, "Failed to get data usage: " + e);
return -1;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4f2b14e335b..be889e47db01 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1781,6 +1781,14 @@ public final class DisplayManagerService extends SystemService {
return mDisplayModeDirector.getModeSwitchingType();
}
+ private boolean getDisplayDecorationSupportInternal(int displayId) {
+ final IBinder displayToken = getDisplayToken(displayId);
+ if (null == displayToken) {
+ return false;
+ }
+ return SurfaceControl.getDisplayDecorationSupport(displayToken);
+ }
+
private void setBrightnessConfigurationForDisplayInternal(
@Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
String packageName) {
@@ -3441,6 +3449,16 @@ public final class DisplayManagerService extends SystemService {
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override // Binder call
+ public boolean getDisplayDecorationSupport(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getDisplayDecorationSupportInternal(displayId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
private static boolean isValidBrightness(float brightness) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2dfaa8bad380..c4d02c7abafb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -50,6 +50,8 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
+import android.util.MutableFloat;
+import android.util.MutableInt;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.Display;
@@ -1354,6 +1356,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Animate the screen brightness when the screen is on or dozing.
// Skip the animation when the screen is off or suspended or transition to/from VR.
+ boolean brightnessAdjusted = false;
if (!mPendingScreenOff) {
if (mSkipScreenOnBrightnessRamp) {
if (state == Display.STATE_ON) {
@@ -1446,15 +1449,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
}
- notifyBrightnessChanged(brightnessState, userInitiatedChange,
+ notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
hadUserBrightnessPoint);
}
// We save the brightness info *after* the brightness setting has been changed and
// adjustments made so that the brightness info reflects the latest value.
- saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
} else {
- saveBrightnessInfo(getScreenBrightnessSetting());
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting());
+ }
+
+ if (brightnessAdjusted) {
+ postBrightnessChangeRunnable();
}
// Log any changes to what is currently driving the brightness setting.
@@ -1570,31 +1577,50 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
- mCachedBrightnessInfo.brightness,
- mCachedBrightnessInfo.adjustedBrightness,
- mCachedBrightnessInfo.brightnessMin,
- mCachedBrightnessInfo.brightnessMax,
- mCachedBrightnessInfo.hbmMode,
- mCachedBrightnessInfo.highBrightnessTransitionPoint);
+ mCachedBrightnessInfo.brightness.value,
+ mCachedBrightnessInfo.adjustedBrightness.value,
+ mCachedBrightnessInfo.brightnessMin.value,
+ mCachedBrightnessInfo.brightnessMax.value,
+ mCachedBrightnessInfo.hbmMode.value,
+ mCachedBrightnessInfo.hbmTransitionPoint.value);
}
}
- private void saveBrightnessInfo(float brightness) {
- saveBrightnessInfo(brightness, brightness);
+ private boolean saveBrightnessInfo(float brightness) {
+ return saveBrightnessInfo(brightness, brightness);
}
- private void saveBrightnessInfo(float brightness, float adjustedBrightness) {
+ private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
synchronized (mCachedBrightnessInfo) {
- mCachedBrightnessInfo.brightness = brightness;
- mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness;
- mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
- mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
- mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
- mCachedBrightnessInfo.highBrightnessTransitionPoint =
- mHbmController.getTransitionPoint();
+ boolean changed = false;
+
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ mHbmController.getCurrentBrightnessMin());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ mHbmController.getCurrentBrightnessMax());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
+
+ return changed;
}
}
+ void postBrightnessChangeRunnable() {
+ mHandler.post(mOnBrightnessChangeRunnable);
+ }
+
private HighBrightnessModeController createHbmControllerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
@@ -1607,7 +1633,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
() -> {
sendUpdatePowerStateLocked();
- mHandler.post(mOnBrightnessChangeRunnable);
+ postBrightnessChangeRunnable();
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
@@ -2109,7 +2135,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void setCurrentScreenBrightness(float brightnessValue) {
if (brightnessValue != mCurrentScreenBrightnessSetting) {
mCurrentScreenBrightnessSetting = brightnessValue;
- mHandler.post(mOnBrightnessChangeRunnable);
+ postBrightnessChangeRunnable();
}
}
@@ -2161,7 +2187,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return true;
}
- private void notifyBrightnessChanged(float brightness, boolean userInitiated,
+ private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
@@ -2271,16 +2297,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
synchronized (mCachedBrightnessInfo) {
- pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness);
+ pw.println(" mCachedBrightnessInfo.brightness=" +
+ mCachedBrightnessInfo.brightness.value);
pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
- mCachedBrightnessInfo.adjustedBrightness);
+ mCachedBrightnessInfo.adjustedBrightness.value);
pw.println(" mCachedBrightnessInfo.brightnessMin=" +
- mCachedBrightnessInfo.brightnessMin);
+ mCachedBrightnessInfo.brightnessMin.value);
pw.println(" mCachedBrightnessInfo.brightnessMax=" +
- mCachedBrightnessInfo.brightnessMax);
- pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode);
- pw.println(" mCachedBrightnessInfo.highBrightnessTransitionPoint=" +
- mCachedBrightnessInfo.highBrightnessTransitionPoint);
+ mCachedBrightnessInfo.brightnessMax.value);
+ pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
+ pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
+ mCachedBrightnessInfo.hbmTransitionPoint.value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2698,11 +2725,31 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
static class CachedBrightnessInfo {
- public float brightness;
- public float adjustedBrightness;
- public float brightnessMin;
- public float brightnessMax;
- public int hbmMode;
- public float highBrightnessTransitionPoint;
+ public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat adjustedBrightness =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMin =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMax =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+ public MutableFloat hbmTransitionPoint =
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+
+ public boolean checkAndSetFloat(MutableFloat mf, float f) {
+ if (mf.value != f) {
+ mf.value = f;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean checkAndSetInt(MutableInt mi, int i) {
+ if (mi.value != i) {
+ mi.value = i;
+ return true;
+ }
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index e40d86a55b77..9fb1d8e744fb 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -896,8 +896,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
info.packageName = mPackage;
info.attributionTag = mAttributionTag;
info.type = (mUid == Process.SYSTEM_UID)
- ? HostEndpointInfo.Type.TYPE_FRAMEWORK
- : HostEndpointInfo.Type.TYPE_APP;
+ ? HostEndpointInfo.Type.FRAMEWORK
+ : HostEndpointInfo.Type.APP;
mContextHubProxy.onHostEndpointConnected(info);
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 19f7c4d8da37..c199bb30a6d3 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -152,6 +152,14 @@ import java.util.concurrent.atomic.AtomicInteger;
@Override
/* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ ContextHubStatsLog.write(
+ ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED,
+ nanoAppBinary.getNanoAppId(),
+ nanoAppBinary.getNanoAppVersion(),
+ ContextHubStatsLog
+ .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_LOAD,
+ toStatsTransactionResult(result));
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
// NOTE: The legacy JNI code used to do a query right after a load success
// to synchronize the service cache. Instead store the binary that was
@@ -200,6 +208,13 @@ import java.util.concurrent.atomic.AtomicInteger;
@Override
/* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ ContextHubStatsLog.write(
+ ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED, nanoAppId,
+ 0 /* nanoappVersion */,
+ ContextHubStatsLog
+ .CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_TYPE__TYPE_UNLOAD,
+ toStatsTransactionResult(result));
+
if (result == ContextHubTransaction.RESULT_SUCCESS) {
mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
}
@@ -477,6 +492,30 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
+ private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
+ switch (result) {
+ case ContextHubTransaction.RESULT_SUCCESS:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_SUCCESS;
+ case ContextHubTransaction.RESULT_FAILED_BAD_PARAMS:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BAD_PARAMS;
+ case ContextHubTransaction.RESULT_FAILED_UNINITIALIZED:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNINITIALIZED;
+ case ContextHubTransaction.RESULT_FAILED_BUSY:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_BUSY;
+ case ContextHubTransaction.RESULT_FAILED_AT_HUB:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_AT_HUB;
+ case ContextHubTransaction.RESULT_FAILED_TIMEOUT:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_TIMEOUT;
+ case ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_SERVICE_INTERNAL_FAILURE;
+ case ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE:
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_HAL_UNAVAILABLE;
+ case ContextHubTransaction.RESULT_FAILED_UNKNOWN:
+ default: /* fall through */
+ return ContextHubStatsLog.CHRE_CODE_DOWNLOAD_TRANSACTED__TRANSACTION_RESULT__TRANSACTION_RESULT_FAILED_UNKNOWN;
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(100);
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index cc5aaf4f7f45..f84b68e7c556 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -453,8 +453,13 @@ public abstract class IContextHubWrapper {
public int sendMessageToContextHub(
short hostEndpointId, int contextHubId, NanoAppMessage message)
throws RemoteException {
- return toTransactionResult(mHub.sendMessageToHub(contextHubId,
- ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message)));
+ try {
+ mHub.sendMessageToHub(contextHubId,
+ ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message));
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
@@ -462,31 +467,55 @@ public abstract class IContextHubWrapper {
int transactionId) throws RemoteException {
android.hardware.contexthub.NanoappBinary aidlNanoAppBinary =
ContextHubServiceUtil.createAidlNanoAppBinary(binary);
- return toTransactionResult(
- mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId));
+ try {
+ mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int unloadNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.unloadNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int enableNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.enableNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int disableNanoapp(int contextHubId, long nanoappId, int transactionId)
throws RemoteException {
- return toTransactionResult(mHub.disableNanoapp(contextHubId, nanoappId, transactionId));
+ try {
+ mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
@ContextHubTransaction.Result
public int queryNanoapps(int contextHubId) throws RemoteException {
- return toTransactionResult(mHub.queryNanoapps(contextHubId));
+ try {
+ mHub.queryNanoapps(contextHubId);
+ return ContextHubTransaction.RESULT_SUCCESS;
+ } catch (RemoteException e) {
+ return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ }
}
public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
@@ -494,12 +523,6 @@ public abstract class IContextHubWrapper {
mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
}
- @ContextHubTransaction.Result
- private int toTransactionResult(boolean success) {
- return success ? ContextHubTransaction.RESULT_SUCCESS
- : ContextHubTransaction.RESULT_FAILED_UNKNOWN;
- }
-
private void onSettingChanged(byte setting, boolean enabled) {
try {
mHub.onSettingChanged(setting, enabled);
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index a52c9cefb27d..5093f5dee55e 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -131,8 +131,8 @@ public class GeofenceManager extends
return mPermitted;
}
- boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -242,7 +242,7 @@ public class GeofenceManager extends
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
GeofenceManager.this.onLocationPermissionsChanged(packageName);
}
@@ -494,7 +494,7 @@ public class GeofenceManager extends
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- void onLocationPermissionsChanged(String packageName) {
+ void onLocationPermissionsChanged(@Nullable String packageName) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 5e6ae68c02f2..a54047665aba 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -119,8 +119,8 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
*/
protected void onGnssListenerUnregister() {}
- boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -197,7 +197,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
}
@@ -390,7 +390,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onLocationPermissionsChanged(String packageName) {
+ private void onLocationPermissionsChanged(@Nullable String packageName) {
updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
index 2df21017156d..557ecda2ef4c 100644
--- a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
@@ -18,6 +18,7 @@ package com.android.server.location.injector;
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
import com.android.server.location.LocationPermissions;
@@ -36,9 +37,10 @@ public abstract class LocationPermissionsHelper {
public interface LocationPermissionsListener {
/**
- * Called when something has changed about location permissions for the given package.
+ * Called when something has changed about location permissions for the given package. A
+ * null package indicates this affects every package.
*/
- void onLocationPermissionsChanged(String packageName);
+ void onLocationPermissionsChanged(@Nullable String packageName);
/**
* Called when something has changed about location permissions for the given uid.
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 1ba32ac1eec2..d42e2c63e80d 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -509,8 +509,8 @@ public class LocationProviderManager extends
}
@GuardedBy("mLock")
- final boolean onLocationPermissionsChanged(String packageName) {
- if (getIdentity().getPackageName().equals(packageName)) {
+ final boolean onLocationPermissionsChanged(@Nullable String packageName) {
+ if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
return onLocationPermissionsChanged();
}
@@ -915,30 +915,19 @@ public class LocationProviderManager extends
return null;
}
+ // acquire a wakelock for non-passive requests
+ boolean useWakeLock =
+ getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL;
+
// deliver location
return new ListenerOperation<LocationTransport>() {
- private boolean mUseWakeLock;
-
@Override
public void onPreExecute() {
- mUseWakeLock = false;
-
- // don't acquire a wakelock for passive requests or for mock locations
- if (getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL) {
- final int size = locationResult.size();
- for (int i = 0; i < size; ++i) {
- if (!locationResult.get(i).isMock()) {
- mUseWakeLock = true;
- break;
- }
- }
- }
-
// update last delivered location
setLastDeliveredLocation(locationResult.getLastLocation());
- if (mUseWakeLock) {
+ if (useWakeLock) {
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
}
}
@@ -955,14 +944,14 @@ public class LocationProviderManager extends
}
listener.deliverOnLocationChanged(deliverLocationResult,
- mUseWakeLock ? mWakeLockReleaser : null);
+ useWakeLock ? mWakeLockReleaser : null);
EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@Override
public void onPostExecute(boolean success) {
- if (!success && mUseWakeLock) {
+ if (!success && useWakeLock) {
mWakeLock.release();
}
@@ -1355,7 +1344,7 @@ public class LocationProviderManager extends
private final LocationPermissionsListener mLocationPermissionsListener =
new LocationPermissionsListener() {
@Override
- public void onLocationPermissionsChanged(String packageName) {
+ public void onLocationPermissionsChanged(@Nullable String packageName) {
LocationProviderManager.this.onLocationPermissionsChanged(packageName);
}
@@ -2361,7 +2350,7 @@ public class LocationProviderManager extends
}
}
- private void onLocationPermissionsChanged(String packageName) {
+ private void onLocationPermissionsChanged(@Nullable String packageName) {
synchronized (mLock) {
updateRegistrations(
registration -> registration.onLocationPermissionsChanged(packageName));
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 20686322fd3d..755c50df1bef 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -595,6 +595,7 @@ public class NotificationManagerService extends SystemService {
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private boolean mLockScreenAllowSecureNotifications = true;
+ boolean mAllowFgsDismissal = false;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
@@ -1137,8 +1138,9 @@ public class NotificationManagerService extends SystemService {
id = r.getSbn().getId();
}
}
+ int mustNotHaveFlags = FLAG_ONGOING_EVENT;
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+ mustNotHaveFlags,
true, userId, REASON_CANCEL, nv.rank, nv.count,null);
nv.recycle();
}
@@ -2411,7 +2413,7 @@ public class NotificationManagerService extends SystemService {
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
- private void registerDeviceConfigChange() {
+ void registerDeviceConfigChange() {
mDeviceConfigChangedListener = properties -> {
if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
return;
@@ -2440,9 +2442,19 @@ public class NotificationManagerService extends SystemService {
} else if ("false".equals(value)) {
mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
}
+ } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
+ String value = properties.getString(name, null);
+ if ("true".equals(value)) {
+ mAllowFgsDismissal = true;
+ } else if ("false".equals(value)) {
+ mAllowFgsDismissal = false;
+ }
}
}
};
+ mAllowFgsDismissal = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
new HandlerExecutor(mHandler),
@@ -3343,8 +3355,7 @@ public class NotificationManagerService extends SystemService {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
- // Calling from user space, don't allow the canceling of actively
- // running foreground services.
+ // Don't allow the app to cancel active FGS notifications
cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
REASON_APP_CANCEL_ALL, null);
@@ -4414,8 +4425,9 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
- cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
- FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
+ int mustNotHaveFlags = FLAG_ONGOING_EVENT;
+ cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
+ mustNotHaveFlags,
true,
userId, REASON_LISTENER_CANCEL, info);
}
@@ -6132,12 +6144,11 @@ public class NotificationManagerService extends SystemService {
return;
}
StatusBarNotification sbn = r.getSbn();
- // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
+ // NoMan adds flags FLAG_ONGOING_EVENT when it sees
// FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
// FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
// initially *and* force remove FLAG_FOREGROUND_SERVICE.
- sbn.getNotification().flags =
- (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
+ sbn.getNotification().flags = (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE);
}
};
@@ -6910,7 +6921,6 @@ public class NotificationManagerService extends SystemService {
r.getKey(), true /* notifSuppressed */, isBubbleSuppressed);
return;
}
-
if ((r.getNotification().flags & mMustHaveFlags) != mMustHaveFlags) {
return;
}
@@ -6918,19 +6928,25 @@ public class NotificationManagerService extends SystemService {
return;
}
- // Bubbled children get to stick around if the summary was manually cancelled
- // (user removed) from systemui.
- FlagChecker childrenFlagChecker = null;
- if (mReason == REASON_CANCEL
- || mReason == REASON_CLICK
- || mReason == REASON_CANCEL_ALL) {
- childrenFlagChecker = (flags) -> {
- if ((flags & FLAG_BUBBLE) != 0) {
+ FlagChecker childrenFlagChecker = (flags) -> {
+ if (mReason == REASON_CANCEL
+ || mReason == REASON_CLICK
+ || mReason == REASON_CANCEL_ALL) {
+ // Bubbled children get to stick around if the summary was manually
+ // cancelled (user removed) from systemui.
+ if ((flags & FLAG_BUBBLE) != 0) {
+ return false;
+ }
+ } else if (mReason == REASON_APP_CANCEL) {
+ if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ }
+ if ((flags & mMustNotHaveFlags) != 0) {
return false;
}
return true;
};
- }
// Cancel the notification.
boolean wasPosted = removeFromNotificationListsLocked(r);
@@ -7141,8 +7157,10 @@ public class NotificationManagerService extends SystemService {
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= FLAG_ONGOING_EVENT
- | FLAG_NO_CLEAR;
+ notification.flags |= FLAG_NO_CLEAR;
+ if (!mAllowFgsDismissal) {
+ notification.flags |= FLAG_ONGOING_EVENT;
+ }
}
mRankingHelper.extractSignals(r);
@@ -7417,13 +7435,20 @@ public class NotificationManagerService extends SystemService {
mSummaryByGroupKey.put(group, r);
}
+ FlagChecker childrenFlagChecker = (flags) -> {
+ if ((flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ return false;
+ }
+ return true;
+ };
+
// Clear out group children of the old notification if the update
// causes the group summary to go away. This happens when the old
// notification was a summary and the new one isn't, or when the old
// notification was a summary and its group key changed.
if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */,
- null, REASON_APP_CANCEL);
+ childrenFlagChecker, REASON_APP_CANCEL);
}
}
@@ -9042,7 +9067,6 @@ public class NotificationManagerService extends SystemService {
final StatusBarNotification childSbn = childR.getSbn();
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
childR.getGroupKey().equals(parentNotification.getGroupKey())
- && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
&& (flagChecker == null || flagChecker.apply(childR.getFlags()))
&& (!childR.getChannel().isImportantConversation()
|| reason != REASON_CANCEL)) {
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index dd661306d687..fcf4a0279246 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -49,8 +49,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -309,10 +307,6 @@ public interface Computer {
@Nullable
String getRenamedPackage(@NonNull String packageName);
- @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
- @NonNull
- WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries();
-
/**
* @return set of packages to notify
*/
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2f4f2715f2b6..e37aaa5a9509 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3490,9 +3490,8 @@ public class ComputerEngine implements Computer {
return mSettings.getRenamedPackageLPr(packageName);
}
- @NonNull
- @Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
+ private WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ getSharedLibraries() {
return mSharedLibraries.getAll();
}
@@ -4788,7 +4787,7 @@ public class ComputerEngine implements Computer {
@Override
public List<PackageStateInternal> findSharedNonSystemLibraries(
@NonNull PackageStateInternal pkgSetting) {
- List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+ List<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
if (!deps.isEmpty()) {
List<PackageStateInternal> retValue = new ArrayList<>();
for (SharedLibraryInfo info : deps) {
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index bd730e904f7e..529aca3632ba 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -46,8 +46,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -269,14 +267,6 @@ public final class ComputerLocked extends ComputerEngine {
@NonNull
@Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
- synchronized (mLock) {
- return super.getSharedLibraries();
- }
- }
-
- @NonNull
- @Override
public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
synchronized (mLock) {
return super.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index e6ff836fce1c..52309ce45e30 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -47,8 +47,6 @@ import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -703,14 +701,6 @@ public final class ComputerTracker implements Computer {
@NonNull
@Override
- public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
- try (ThreadComputer current = snapshot()) {
- return current.mComputer.getSharedLibraries();
- }
- }
-
- @NonNull
- @Override
public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
try (ThreadComputer current = snapshot()) {
return current.mComputer.getNotifyPackagesForReplacedReceived(packages);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index eac38af57784..dcad3ecba6af 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -379,7 +379,7 @@ final class DexOptHelper {
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date.
- Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
+ Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 27b6282055a2..9302aaddcdb2 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -894,8 +894,6 @@ final class InstallPackageHelper {
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
- final Map<String, PackageSetting> lastStaticSharedLibSettings =
- new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
try {
@@ -955,35 +953,22 @@ final class InstallPackageHelper {
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null) {
- final PackageSetting staticSharedLibLatestVersionSetting =
- mSharedLibraries.getStaticSharedLibLatestVersionSetting(result);
- if (staticSharedLibLatestVersionSetting != null) {
- lastStaticSharedLibSettings.put(
- result.mPkgSetting.getPkg().getPackageName(),
- staticSharedLibLatestVersionSetting);
- }
- }
} catch (PackageManagerException e) {
request.mInstallResult.setError("Scanning Failed.", e);
return;
}
}
- ReconcileRequest
- reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
- installResults,
- prepareResults,
- mSharedLibraries.getAll(),
- Collections.unmodifiableMap(mPm.mPackages), versionInfos,
- lastStaticSharedLibSettings);
+ ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
+ installResults, prepareResults,
+ Collections.unmodifiableMap(mPm.mPackages), versionInfos);
CommitRequest commitRequest = null;
synchronized (mPm.mLock) {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
- reconcileRequest, mPm.mSettings.getKeySetManagerService(),
- mPm.mInjector);
+ reconcileRequest, mSharedLibraries,
+ mPm.mSettings.getKeySetManagerService());
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.mInstallResult.setError("Reconciliation failed...", e);
@@ -3586,15 +3571,12 @@ final class InstallPackageHelper {
final String pkgName = scanResult.mPkgSetting.getPackageName();
final ReconcileRequest reconcileRequest = new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
- mSharedLibraries.getAll(), mPm.mPackages,
- Collections.singletonMap(pkgName,
- mPm.getSettingsVersionForPackage(parsedPackage)),
+ mPm.mPackages,
Collections.singletonMap(pkgName,
- mSharedLibraries.getStaticSharedLibLatestVersionSetting(
- scanResult)));
+ mPm.getSettingsVersionForPackage(parsedPackage)));
final Map<String, ReconciledPackage> reconcileResult =
ReconcilePackageUtils.reconcilePackages(reconcileRequest,
- mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+ mSharedLibraries, mPm.mSettings.getKeySetManagerService());
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(reconcileResult.get(pkgName),
mPm.mUserManager.getUserIds());
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 1bdc9f3cf850..c219f80ac9c5 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -40,7 +40,7 @@ per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
-per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com, jgalenson@google.com
+per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com
# shortcuts
per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5a250047da8f..67f6b123d99b 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -42,8 +42,8 @@ import java.util.Map;
final class ReconcilePackageUtils {
public static Map<String, ReconciledPackage> reconcilePackages(
- final ReconcileRequest request, KeySetManagerService ksms,
- PackageManagerServiceInjector injector)
+ final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
+ KeySetManagerService ksms)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -67,11 +67,10 @@ final class ReconcilePackageUtils {
// in the first pass, we'll build up the set of incoming shared libraries
final List<SharedLibraryInfo> allowedSharedLibInfos =
- SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
- request.mSharedLibrarySource);
+ sharedLibraries.getAllowedSharedLibInfos(scanResult);
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
- if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+ if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap(
incomingSharedLibraries, info)) {
throw new ReconcileFailure("Shared Library " + info.getName()
+ " is being installed twice in this set!");
@@ -113,7 +112,8 @@ final class ReconcilePackageUtils {
final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
final PackageSetting lastStaticSharedLibSetting =
- request.mLastStaticSharedLibSettings.get(installPackageName);
+ scanResult.mStaticSharedLibraryInfo == null ? null
+ : sharedLibraries.getStaticSharedLibLatestVersionSetting(scanResult);
final PackageSetting signatureCheckPs =
(prepareResult != null && lastStaticSharedLibSetting != null)
? lastStaticSharedLibSetting
@@ -264,11 +264,9 @@ final class ReconcilePackageUtils {
}
try {
result.get(installPackageName).mCollectedSharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- scanResult.mRequest.mParsedPackage,
- combinedPackages, request.mSharedLibrarySource,
- incomingSharedLibraries, injector.getCompatibility());
-
+ sharedLibraries.collectSharedLibraryInfos(
+ scanResult.mRequest.mParsedPackage, combinedPackages,
+ incomingSharedLibraries);
} catch (PackageManagerException e) {
throw new ReconcileFailure(e.error, e.getMessage());
}
diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java
index 31881388e6ec..9e4e986d1fb5 100644
--- a/services/core/java/com/android/server/pm/ReconcileRequest.java
+++ b/services/core/java/com/android/server/pm/ReconcileRequest.java
@@ -16,10 +16,7 @@
package com.android.server.pm;
-import android.content.pm.SharedLibraryInfo;
-
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.utils.WatchedLongSparseArray;
import java.util.Collections;
import java.util.Map;
@@ -37,38 +34,29 @@ final class ReconcileRequest {
public final Map<String, ScanResult> mScannedPackages;
public final Map<String, AndroidPackage> mAllPackages;
- public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibrarySource;
public final Map<String, InstallArgs> mInstallArgs;
public final Map<String, PackageInstalledInfo> mInstallResults;
public final Map<String, PrepareResult> mPreparedPackages;
public final Map<String, Settings.VersionInfo> mVersionInfos;
- public final Map<String, PackageSetting> mLastStaticSharedLibSettings;
ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
Map<String, PrepareResult> preparedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
- Map<String, Settings.VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ Map<String, Settings.VersionInfo> versionInfos) {
mScannedPackages = scannedPackages;
mInstallArgs = installArgs;
mInstallResults = installResults;
mPreparedPackages = preparedPackages;
- mSharedLibrarySource = sharedLibrarySource;
mAllPackages = allPackages;
mVersionInfos = versionInfos;
- mLastStaticSharedLibSettings = lastStaticSharedLibSettings;
}
ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
- Map<String, Settings.VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ Map<String, Settings.VersionInfo> versionInfos) {
this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
- Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
- lastStaticSharedLibSettings);
+ Collections.emptyMap(), allPackages, versionInfos);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 21df5a9c1a45..7085682662e6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4863,11 +4863,11 @@ public final class Settings implements Watchable, Snappable {
pw.print(userState.isInstantApp());
pw.print(" virtual=");
pw.println(userState.isVirtualPreload());
- pw.print(" installReason=");
+ pw.print(" installReason=");
pw.println(userState.getInstallReason());
final PackageUserStateInternal pus = ps.readUserState(user.id);
- pw.print(" firstInstallTime=");
+ pw.print(" firstInstallTime=");
date.setTime(pus.getFirstInstallTime());
pw.println(sdf.format(date));
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 0055f4eb6446..aa230508287e 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -16,19 +16,27 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.VersionedPackage;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArraySet;
+import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -36,8 +44,10 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
@@ -47,10 +57,13 @@ import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.Watcher;
+import libcore.util.HexEncoding;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
@@ -62,6 +75,24 @@ import java.util.function.BiConsumer;
* Current known shared libraries on the device.
*/
public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable {
+ private static final boolean DEBUG_SHARED_LIBRARIES = false;
+
+ /**
+ * Apps targeting Android S and above need to declare dependencies to the public native
+ * shared libraries that are defined by the device maker using {@code uses-native-library} tag
+ * in its {@code AndroidManifest.xml}.
+ *
+ * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
+ * the package manager rejects to install the app. The dependency can be specified as optional
+ * using {@code android:required} attribute in the tag, in which case failing to satisfy the
+ * dependency doesn't stop the installation.
+ * <p>Once installed, an app is provided with only the native shared libraries that are
+ * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
+ * in the app manifest will fail even if it actually exists on the device.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
// TODO(b/200588896): remove PMS dependency
private final PackageManagerService mPm;
@@ -493,10 +524,8 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
@Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
@NonNull Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
- final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- pkgSetting.getPkg(), availablePackages, mSharedLibraries,
- null /* newLibraries */, mInjector.getCompatibility());
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
+ pkgSetting.getPkg(), availablePackages, null /* newLibraries */);
executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mPm.mUserManager.getUserIds());
}
@@ -735,6 +764,234 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
}
/**
+ * Compare the newly scanned package with current system state to see which of its declared
+ * shared libraries should be allowed to be added to the system.
+ */
+ List<SharedLibraryInfo> getAllowedSharedLibInfos(ScanResult scanResult) {
+ // Let's used the parsed package as scanResult.pkgSetting may be null
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+ if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
+ && scanResult.mDynamicSharedLibraryInfos == null) {
+ return null;
+ }
+
+ // Any app can add new SDKs and static shared libraries.
+ if (scanResult.mSdkSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
+ }
+ if (scanResult.mStaticSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
+ }
+ final boolean hasDynamicLibraries = parsedPackage.isSystem()
+ && scanResult.mDynamicSharedLibraryInfos != null;
+ if (!hasDynamicLibraries) {
+ return null;
+ }
+ final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
+ .isUpdatedSystemApp();
+ // We may not yet have disabled the updated package yet, so be sure to grab the
+ // current setting if that's the case.
+ final PackageSetting updatedSystemPs = isUpdatedSystemApp
+ ? scanResult.mRequest.mDisabledPkgSetting == null
+ ? scanResult.mRequest.mOldPkgSetting
+ : scanResult.mRequest.mDisabledPkgSetting
+ : null;
+ if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
+ || updatedSystemPs.getPkg().getLibraryNames() == null)) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " declares libraries that are not declared on the system image; skipping");
+ return null;
+ }
+ final ArrayList<SharedLibraryInfo> infos =
+ new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
+ for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
+ final String name = info.getName();
+ if (isUpdatedSystemApp) {
+ // New library entries can only be added through the
+ // system image. This is important to get rid of a lot
+ // of nasty edge cases: for example if we allowed a non-
+ // system update of the app to add a library, then uninstalling
+ // the update would make the library go away, and assumptions
+ // we made such as through app install filtering would now
+ // have allowed apps on the device which aren't compatible
+ // with it. Better to just have the restriction here, be
+ // conservative, and create many fewer cases that can negatively
+ // impact the user experience.
+ if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " declares library " + name
+ + " that is not declared on system image; skipping");
+ continue;
+ }
+ }
+ synchronized (mPm.mLock) {
+ if (getSharedLibraryInfo(name, SharedLibraryInfo.VERSION_UNDEFINED) != null) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+ + name + " that already exists; skipping");
+ continue;
+ }
+ }
+ infos.add(info);
+ }
+ return infos;
+ }
+
+ /**
+ * Collects shared library infos that are being used by the given package.
+ *
+ * @param pkg The package using shared libraries.
+ * @param availablePackages The available packages which are installed and being installed,
+ * @param newLibraries Shared libraries defined by packages which are being installed.
+ * @return A list of shared library infos
+ */
+ ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(@Nullable AndroidPackage pkg,
+ @NonNull Map<String, AndroidPackage> availablePackages,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+ throws PackageManagerException {
+ if (pkg == null) {
+ return null;
+ }
+ final PlatformCompat platformCompat = mInjector.getCompatibility();
+ // The collection used here must maintain the order of addition (so
+ // that libraries are searched in the correct order) and must have no
+ // duplicates.
+ ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
+ if (!pkg.getUsesLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+ pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
+ availablePackages, newLibraries);
+ }
+ if (!pkg.getUsesStaticLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
+ pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
+ pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, newLibraries);
+ }
+ if (!pkg.getUsesOptionalLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
+ pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, newLibraries);
+ }
+ if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
+ pkg.getPackageName(), pkg.getTargetSdkVersion())) {
+ if (!pkg.getUsesNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
+ null, pkg.getPackageName(), "native shared", true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ newLibraries);
+ }
+ if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
+ null, null, pkg.getPackageName(), "native shared", false,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ newLibraries);
+ }
+ }
+ if (!pkg.getUsesSdkLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
+ pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+ availablePackages, newLibraries);
+ }
+ return usesLibraryInfos;
+ }
+
+ private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
+ @NonNull List<String> requestedLibraries,
+ @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
+ @NonNull String packageName, @NonNull String libraryType, boolean required,
+ int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+ @NonNull final Map<String, AndroidPackage> availablePackages,
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
+ throws PackageManagerException {
+ final int libCount = requestedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ final String libName = requestedLibraries.get(i);
+ final long libVersion = requiredVersions != null ? requiredVersions[i]
+ : SharedLibraryInfo.VERSION_UNDEFINED;
+ final SharedLibraryInfo libraryInfo;
+ synchronized (mPm.mLock) {
+ libraryInfo = SharedLibraryUtils.getSharedLibraryInfo(
+ libName, libVersion, mSharedLibraries, newLibraries);
+ }
+ if (libraryInfo == null) {
+ if (required) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
+ } else if (DEBUG_SHARED_LIBRARIES) {
+ Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+ + " library " + libName + "; ignoring!");
+ }
+ } else {
+ if (requiredVersions != null && requiredCertDigests != null) {
+ if (libraryInfo.getLongVersion() != requiredVersions[i]) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + " version "
+ + libraryInfo.getLongVersion() + "; failing!");
+ }
+ AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
+ SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
+ if (libPkg == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library; failing!");
+ }
+ final String[] expectedCertDigests = requiredCertDigests[i];
+ if (expectedCertDigests.length > 1) {
+ // For apps targeting O MR1 we require explicit enumeration of all certs.
+ final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
+ ? PackageUtils.computeSignaturesSha256Digests(
+ libPkg.getSignatures())
+ : PackageUtils.computeSignaturesSha256Digests(
+ new Signature[]{libPkg.getSignatures()[0]});
+
+ // Take a shortcut if sizes don't match. Note that if an app doesn't
+ // target O we don't parse the "additional-certificate" tags similarly
+ // how we only consider all certs only for apps targeting O (see above).
+ // Therefore, the size check is safe to make.
+ if (expectedCertDigests.length != libCertDigests.length) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+
+ // Use a predictable order as signature order may vary
+ Arrays.sort(libCertDigests);
+ Arrays.sort(expectedCertDigests);
+
+ final int certCount = libCertDigests.length;
+ for (int j = 0; j < certCount; j++) {
+ if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+ }
+ } else {
+ // lib signing cert could have rotated beyond the one expected, check to see
+ // if the new one has been blessed by the old
+ byte[] digestBytes = HexEncoding.decode(
+ expectedCertDigests[0], false /* allowSingleChar */);
+ if (!libPkg.hasSha256Certificate(digestBytes)) {
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
+ }
+ }
+ }
+ if (outUsedLibraries == null) {
+ outUsedLibraries = new ArrayList<>();
+ }
+ outUsedLibraries.add(libraryInfo);
+ }
+ }
+ return outUsedLibraries;
+ }
+
+ /**
* Dump all shared libraries.
*/
@GuardedBy("mPm.mLock")
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
deleted file mode 100644
index dd8fad0970fa..000000000000
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-
-import static com.android.server.pm.PackageManagerService.TAG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.os.Build;
-import android.util.PackageUtils;
-import android.util.Slog;
-
-import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import libcore.util.HexEncoding;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-final class SharedLibraryHelper {
- private static final boolean DEBUG_SHARED_LIBRARIES = false;
-
- /**
- * Apps targeting Android S and above need to declare dependencies to the public native
- * shared libraries that are defined by the device maker using {@code uses-native-library} tag
- * in its {@code AndroidManifest.xml}.
- *
- * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist,
- * the package manager rejects to install the app. The dependency can be specified as optional
- * using {@code android:required} attribute in the tag, in which case failing to satisfy the
- * dependency doesn't stop the installation.
- * <p>Once installed, an app is provided with only the native shared libraries that are
- * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear
- * in the app manifest will fail even if it actually exists on the device.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
- private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
-
- /**
- * Compare the newly scanned package with current system state to see which of its declared
- * shared libraries should be allowed to be added to the system.
- */
- public static List<SharedLibraryInfo> getAllowedSharedLibInfos(
- ScanResult scanResult,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
- // Let's used the parsed package as scanResult.pkgSetting may be null
- final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
- if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
- && scanResult.mDynamicSharedLibraryInfos == null) {
- return null;
- }
-
- // Any app can add new SDKs and static shared libraries.
- if (scanResult.mSdkSharedLibraryInfo != null) {
- return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
- }
- if (scanResult.mStaticSharedLibraryInfo != null) {
- return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
- }
- final boolean hasDynamicLibraries = parsedPackage.isSystem()
- && scanResult.mDynamicSharedLibraryInfos != null;
- if (!hasDynamicLibraries) {
- return null;
- }
- final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
- .isUpdatedSystemApp();
- // We may not yet have disabled the updated package yet, so be sure to grab the
- // current setting if that's the case.
- final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? scanResult.mRequest.mDisabledPkgSetting == null
- ? scanResult.mRequest.mOldPkgSetting
- : scanResult.mRequest.mDisabledPkgSetting
- : null;
- if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
- || updatedSystemPs.getPkg().getLibraryNames() == null)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " declares libraries that are not declared on the system image; skipping");
- return null;
- }
- final ArrayList<SharedLibraryInfo> infos =
- new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
- for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
- final String name = info.getName();
- if (isUpdatedSystemApp) {
- // New library entries can only be added through the
- // system image. This is important to get rid of a lot
- // of nasty edge cases: for example if we allowed a non-
- // system update of the app to add a library, then uninstalling
- // the update would make the library go away, and assumptions
- // we made such as through app install filtering would now
- // have allowed apps on the device which aren't compatible
- // with it. Better to just have the restriction here, be
- // conservative, and create many fewer cases that can negatively
- // impact the user experience.
- if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " declares library " + name
- + " that is not declared on system image; skipping");
- continue;
- }
- }
- if (sharedLibExists(
- name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
- + name + " that already exists; skipping");
- continue;
- }
- infos.add(info);
- }
- return infos;
- }
-
- public static boolean sharedLibExists(final String name, final long version,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
- return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
- }
-
- /**
- * Returns false if the adding shared library already exists in the map and so could not be
- * added.
- */
- public static boolean addSharedLibraryToPackageVersionMap(
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
- SharedLibraryInfo library) {
- final String name = library.getName();
- if (target.containsKey(name)) {
- if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
- // We've already added this non-version-specific library to the map.
- return false;
- } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
- // We've already added this version of a version-specific library to the map.
- return false;
- }
- } else {
- target.put(name, new WatchedLongSparseArray<>());
- }
- target.get(name).put(library.getLongVersion(), library);
- return true;
- }
-
- public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
- Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
- PlatformCompat platformCompat) throws PackageManagerException {
- if (pkg == null) {
- return null;
- }
- // The collection used here must maintain the order of addition (so
- // that libraries are searched in the correct order) and must have no
- // duplicates.
- ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
- if (!pkg.getUsesLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
- pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
- availablePackages, existingLibraries, newLibraries);
- }
- if (!pkg.getUsesStaticLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
- pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
- }
- if (!pkg.getUsesOptionalLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
- pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
- }
- if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
- pkg.getPackageName(), pkg.getTargetSdkVersion())) {
- if (!pkg.getUsesNativeLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
- null, pkg.getPackageName(), "native shared", true,
- pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- existingLibraries, newLibraries);
- }
- if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
- null, null, pkg.getPackageName(), "native shared", false,
- pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
- existingLibraries, newLibraries);
- }
- }
- if (!pkg.getUsesSdkLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
- pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
- pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
- availablePackages, existingLibraries, newLibraries);
- }
- return usesLibraryInfos;
- }
-
- public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
- @NonNull List<String> requestedLibraries,
- @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
- @NonNull String packageName, @NonNull String libraryType, boolean required,
- int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
- @NonNull final Map<String, AndroidPackage> availablePackages,
- @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
- throws PackageManagerException {
- final int libCount = requestedLibraries.size();
- for (int i = 0; i < libCount; i++) {
- final String libName = requestedLibraries.get(i);
- final long libVersion = requiredVersions != null ? requiredVersions[i]
- : SharedLibraryInfo.VERSION_UNDEFINED;
- final SharedLibraryInfo libraryInfo =
- getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries);
- if (libraryInfo == null) {
- if (required) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library " + libName + "; failing!");
- } else if (DEBUG_SHARED_LIBRARIES) {
- Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
- + " library " + libName + "; ignoring!");
- }
- } else {
- if (requiredVersions != null && requiredCertDigests != null) {
- if (libraryInfo.getLongVersion() != requiredVersions[i]) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library " + libName + " version "
- + libraryInfo.getLongVersion() + "; failing!");
- }
- AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
- SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
- if (libPkg == null) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable " + libraryType
- + " library; failing!");
- }
- final String[] expectedCertDigests = requiredCertDigests[i];
- if (expectedCertDigests.length > 1) {
- // For apps targeting O MR1 we require explicit enumeration of all certs.
- final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
- ? PackageUtils.computeSignaturesSha256Digests(
- libPkg.getSignatures())
- : PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.getSignatures()[0]});
-
- // Take a shortcut if sizes don't match. Note that if an app doesn't
- // target O we don't parse the "additional-certificate" tags similarly
- // how we only consider all certs only for apps targeting O (see above).
- // Therefore, the size check is safe to make.
- if (expectedCertDigests.length != libCertDigests.length) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
-
- // Use a predictable order as signature order may vary
- Arrays.sort(libCertDigests);
- Arrays.sort(expectedCertDigests);
-
- final int certCount = libCertDigests.length;
- for (int j = 0; j < certCount; j++) {
- if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
- throw new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
- }
- } else {
- // lib signing cert could have rotated beyond the one expected, check to see
- // if the new one has been blessed by the old
- byte[] digestBytes = HexEncoding.decode(
- expectedCertDigests[0], false /* allowSingleChar */);
- if (!libPkg.hasSha256Certificate(digestBytes)) {
- throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed "
- + libraryType + " library; failing!");
- }
- }
- }
- if (outUsedLibraries == null) {
- outUsedLibraries = new ArrayList<>();
- }
- outUsedLibraries.add(libraryInfo);
- }
- }
- return outUsedLibraries;
- }
-
- @Nullable
- public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
- if (newLibraries != null) {
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
- SharedLibraryInfo info = null;
- if (versionedLib != null) {
- info = versionedLib.get(version);
- }
- if (info != null) {
- return info;
- }
- }
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
- if (versionedLib == null) {
- return null;
- }
- return versionedLib.get(version);
- }
-
- public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
- if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
- ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
- Set<String> collectedNames = new HashSet<>();
- for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
- findSharedLibrariesRecursive(info, retValue, collectedNames);
- }
- return retValue;
- } else {
- return Collections.emptyList();
- }
- }
-
- private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
- ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
- if (!collectedNames.contains(info.getName())) {
- collectedNames.add(info.getName());
- collected.add(info);
-
- if (info.getDependencies() != null) {
- for (SharedLibraryInfo dep : info.getDependencies()) {
- findSharedLibrariesRecursive(dep, collected, collectedNames);
- }
- }
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/pm/SharedLibraryUtils.java b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
new file mode 100644
index 000000000000..274870dad6f0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class SharedLibraryUtils {
+
+ /**
+ * Returns false if the adding shared library already exists in the map and so could not be
+ * added.
+ */
+ public static boolean addSharedLibraryToPackageVersionMap(
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
+ SharedLibraryInfo library) {
+ final String name = library.getName();
+ if (target.containsKey(name)) {
+ if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
+ // We've already added this non-version-specific library to the map.
+ return false;
+ } else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
+ // We've already added this version of a version-specific library to the map.
+ return false;
+ }
+ } else {
+ target.put(name, new WatchedLongSparseArray<>());
+ }
+ target.get(name).put(library.getLongVersion(), library);
+ return true;
+ }
+
+ @Nullable
+ public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
+ @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
+ if (newLibraries != null) {
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
+ SharedLibraryInfo info = null;
+ if (versionedLib != null) {
+ info = versionedLib.get(version);
+ }
+ if (info != null) {
+ return info;
+ }
+ }
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
+ if (versionedLib == null) {
+ return null;
+ }
+ return versionedLib.get(version);
+ }
+
+ public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
+ if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
+ ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
+ Set<String> collectedNames = new HashSet<>();
+ for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
+ findSharedLibrariesRecursive(info, retValue, collectedNames);
+ }
+ return retValue;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
+ ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
+ if (!collectedNames.contains(info.getName())) {
+ collectedNames.add(info.getName());
+ collected.add(info);
+
+ if (info.getDependencies() != null) {
+ for (SharedLibraryInfo dep : info.getDependencies()) {
+ findSharedLibrariesRecursive(dep, collected, collectedNames);
+ }
+ }
+ }
+ }
+
+}
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 b15e495a3714..af524db74341 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -217,6 +217,11 @@ final class DefaultPermissionGrantPolicy {
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
}
+ private static final Set<String> NOTIFICATION_PERMISSIONS = new ArraySet<>();
+ static {
+ NOTIFICATION_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
+ }
+
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
private static final String ACTION_TRACK = "com.android.fitness.TRACK";
@@ -378,18 +383,43 @@ final class DefaultPermissionGrantPolicy {
grantPermissionsToSysComponentsAndPrivApps(pm, userId);
grantDefaultSystemHandlerPermissions(pm, userId);
+ grantSignatureAppsNotificationPermissions(pm, userId);
grantDefaultPermissionExceptions(pm, userId);
// Apply delayed state
pm.apply();
}
+ private void grantSignatureAppsNotificationPermissions(PackageManagerWrapper pm, int userId) {
+ Log.i(TAG, "Granting Notification permissions to platform signature apps for user "
+ + userId);
+ List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
+ DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
+ for (PackageInfo pkg : packages) {
+ if (pkg == null || !pkg.applicationInfo.isSystemApp()
+ || !pkg.applicationInfo.isSignedWithPlatformKey()) {
+ continue;
+ }
+ grantRuntimePermissionsForSystemPackage(pm, userId, pkg, NOTIFICATION_PERMISSIONS);
+ }
+
+ }
+
private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
int userId, PackageInfo pkg) {
+ grantRuntimePermissionsForSystemPackage(pm, userId, pkg, null);
+ }
+
+ private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
+ int userId, PackageInfo pkg, Set<String> filterPermissions) {
+ if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+ return;
+ }
Set<String> permissions = new ArraySet<>();
for (String permission : pkg.requestedPermissions) {
final PermissionInfo perm = pm.getPermissionInfo(permission);
- if (perm == null) {
+ if (perm == null
+ || (filterPermissions != null && !filterPermissions.contains(permission))) {
continue;
}
if (perm.isRuntime()) {
@@ -547,23 +577,31 @@ final class DefaultPermissionGrantPolicy {
String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
+ // PermissionController
+ grantSystemFixedPermissionsToSystemPackage(pm,
+ mContext.getPackageManager().getPermissionControllerPackageName(), userId,
+ NOTIFICATION_PERMISSIONS);
+
// Installer
grantSystemFixedPermissionsToSystemPackage(pm,
ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_INSTALLER, userId)),
- userId, STORAGE_PERMISSIONS);
+ userId, STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Verifier
final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_VERIFIER, userId));
grantSystemFixedPermissionsToSystemPackage(pm, verifier, userId, STORAGE_PERMISSIONS);
- grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
+ grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
// SetupWizard
final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ grantSystemFixedPermissionsToSystemPackage(pm, setupWizardPackage, userId,
+ NOTIFICATION_PERMISSIONS);
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)
|| mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE,
0)) {
@@ -585,12 +623,12 @@ final class DefaultPermissionGrantPolicy {
// Media provider
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
- STORAGE_PERMISSIONS);
+ STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Downloads provider
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultProviderAuthorityPackage("downloads", userId), userId,
- STORAGE_PERMISSIONS);
+ STORAGE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Downloads UI
grantSystemFixedPermissionsToSystemPackage(pm,
@@ -649,7 +687,7 @@ final class DefaultPermissionGrantPolicy {
// Cell Broadcast Receiver
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
- userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+ userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Carrier Provisioning Service
grantPermissionsToSystemPackage(pm,
@@ -661,7 +699,7 @@ final class DefaultPermissionGrantPolicy {
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackageForCategory(pm,
Intent.CATEGORY_APP_CALENDAR, userId),
- userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
+ userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Calendar provider
String calendarProvider =
@@ -762,7 +800,8 @@ final class DefaultPermissionGrantPolicy {
grantPermissionsToSystemPackage(pm, packageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
- SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
+ SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
}
@@ -791,7 +830,7 @@ final class DefaultPermissionGrantPolicy {
.addCategory(Intent.CATEGORY_LAUNCHER_APP);
grantPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, homeIntent, userId), userId,
- ALWAYS_LOCATION_PERMISSIONS);
+ ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// Watches
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
@@ -816,7 +855,7 @@ final class DefaultPermissionGrantPolicy {
// Print Spooler
grantSystemFixedPermissionsToSystemPackage(pm, PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
- userId, ALWAYS_LOCATION_PERMISSIONS);
+ userId, ALWAYS_LOCATION_PERMISSIONS, NOTIFICATION_PERMISSIONS);
// EmergencyInfo
grantSystemFixedPermissionsToSystemPackage(pm,
@@ -920,12 +959,13 @@ final class DefaultPermissionGrantPolicy {
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
if (isPhonePermFixed) {
grantSystemFixedPermissionsToSystemPackage(pm, dialerPackage, userId,
- PHONE_PERMISSIONS);
+ PHONE_PERMISSIONS, NOTIFICATION_PERMISSIONS);
} else {
grantPermissionsToSystemPackage(pm, dialerPackage, userId, PHONE_PERMISSIONS);
}
grantPermissionsToSystemPackage(pm, dialerPackage, userId,
- CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+ CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
boolean isAndroidAutomotive =
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
if (isAndroidAutomotive) {
@@ -937,7 +977,8 @@ final class DefaultPermissionGrantPolicy {
String smsPackage, int userId) {
grantPermissionsToSystemPackage(pm, smsPackage, userId,
PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
- STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
+ STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS,
+ NOTIFICATION_PERMISSIONS);
}
private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(PackageManagerWrapper pm,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index cdab91bbaef8..411f3dcc1eb6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -136,7 +136,8 @@ public interface StatusBarManagerInternal {
@Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
- void showTransient(int displayId, @InternalInsetsType int[] types);
+ void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar);
/** @see com.android.internal.statusbar.IStatusBar#abortTransient */
void abortTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 54ec8845b24f..17f5566a9864 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -567,11 +567,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ public void showTransient(int displayId, @InternalInsetsType int[] types,
+ boolean isGestureOnSystemBar) {
getUiState(displayId).showTransient(types);
if (mBar != null) {
try {
- mBar.showTransient(displayId, types);
+ mBar.showTransient(displayId, types, isGestureOnSystemBar);
} catch (RemoteException ex) { }
}
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index e508260746da..63f4c68b11f6 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.IResourceManagerService;
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
@@ -543,6 +544,12 @@ public class TunerResourceManagerService extends SystemService implements IBinde
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump!");
+ return;
+ }
+
synchronized (mLock) {
if (mClientProfiles != null) {
pw.println("ClientProfiles:");
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index a31c56a3b737..a17e79273e41 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -21,6 +21,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,15 +39,19 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -92,6 +97,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
@NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
@NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
+ @NonNull
+ private final List<CarrierPrivilegesListener> mCarrierPrivilegesChangedListeners =
+ new ArrayList<>();
+
@NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot;
public TelephonySubscriptionTracker(
@@ -126,22 +135,71 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
};
}
- /** Registers the receivers, and starts tracking subscriptions. */
+ /**
+ * Registers the receivers, and starts tracking subscriptions.
+ *
+ * <p>Must always be run on the VcnManagementService thread.
+ */
public void register() {
final HandlerExecutor executor = new HandlerExecutor(mHandler);
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED);
- mContext.registerReceiver(
- this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler);
+ mContext.registerReceiver(this, filter, null, mHandler);
mSubscriptionManager.addOnSubscriptionsChangedListener(
executor, mSubscriptionChangedListener);
mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
+
+ registerCarrierPrivilegesListeners();
+ }
+
+ private void registerCarrierPrivilegesListeners() {
+ final HandlerExecutor executor = new HandlerExecutor(mHandler);
+ final int modemCount = mTelephonyManager.getActiveModemCount();
+ try {
+ for (int i = 0; i < modemCount; i++) {
+ CarrierPrivilegesListener carrierPrivilegesListener =
+ new CarrierPrivilegesListener() {
+ @Override
+ public void onCarrierPrivilegesChanged(
+ @NonNull List<String> privilegedPackageNames,
+ @NonNull int[] privilegedUids) {
+ // Re-trigger the synchronous check (which is also very cheap due
+ // to caching in CarrierPrivilegesTracker). This allows consistency
+ // with the onSubscriptionsChangedListener and broadcasts.
+ handleSubscriptionsChanged();
+ }
+ };
+
+ mTelephonyManager.addCarrierPrivilegesListener(
+ i, executor, carrierPrivilegesListener);
+ mCarrierPrivilegesChangedListeners.add(carrierPrivilegesListener);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Encounted exception registering carrier privileges listeners", e);
+ }
}
- /** Unregisters the receivers, and stops tracking subscriptions. */
+ /**
+ * Unregisters the receivers, and stops tracking subscriptions.
+ *
+ * <p>Must always be run on the VcnManagementService thread.
+ */
public void unregister() {
mContext.unregisterReceiver(this);
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
+
+ unregisterCarrierPrivilegesListeners();
+ }
+
+ private void unregisterCarrierPrivilegesListeners() {
+ for (CarrierPrivilegesListener carrierPrivilegesListener :
+ mCarrierPrivilegesChangedListeners) {
+ mTelephonyManager.removeCarrierPrivilegesListener(carrierPrivilegesListener);
+ }
+ mCarrierPrivilegesChangedListeners.clear();
}
/**
@@ -178,8 +236,6 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
// group.
if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
&& mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
- // TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker
-
final TelephonyManager subIdSpecificTelephonyManager =
mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
@@ -214,12 +270,39 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
*/
@Override
public void onReceive(Context context, Intent intent) {
- // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
- // already was for an identified carrier, we can stop waiting for initial load to complete
- if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
- return;
+ switch (intent.getAction()) {
+ case ACTION_CARRIER_CONFIG_CHANGED:
+ handleActionCarrierConfigChanged(context, intent);
+ break;
+ case ACTION_MULTI_SIM_CONFIG_CHANGED:
+ handleActionMultiSimConfigChanged(context, intent);
+ break;
+ default:
+ Slog.v(TAG, "Unknown intent received with action: " + intent.getAction());
}
+ }
+
+ private void handleActionMultiSimConfigChanged(Context context, Intent intent) {
+ unregisterCarrierPrivilegesListeners();
+
+ // Clear invalid slotIds from the mReadySubIdsBySlotId map.
+ final int modemCount = mTelephonyManager.getActiveModemCount();
+ final Iterator<Integer> slotIdIterator = mReadySubIdsBySlotId.keySet().iterator();
+ while (slotIdIterator.hasNext()) {
+ final int slotId = slotIdIterator.next();
+ if (slotId >= modemCount) {
+ slotIdIterator.remove();
+ }
+ }
+
+ registerCarrierPrivilegesListeners();
+ handleSubscriptionsChanged();
+ }
+
+ private void handleActionCarrierConfigChanged(Context context, Intent intent) {
+ // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it
+ // already was for an identified carrier, we can stop waiting for initial load to complete
final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 45769575111f..bd8d13b87125 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,8 +20,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -29,10 +29,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnManager;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.SubscriptionManager;
@@ -76,7 +76,7 @@ class NetworkPriorityClassifier {
public static int calculatePriorityClass(
VcnContext vcnContext,
UnderlyingNetworkRecord networkRecord,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +94,7 @@ class NetworkPriorityClassifier {
}
int priorityIndex = 0;
- for (VcnUnderlyingNetworkPriority nwPriority : underlyingNetworkPriorities) {
+ for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) {
if (checkMatchesPriorityRule(
vcnContext,
nwPriority,
@@ -113,7 +113,7 @@ class NetworkPriorityClassifier {
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesPriorityRule(
VcnContext vcnContext,
- VcnUnderlyingNetworkPriority networkPriority,
+ VcnUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
@@ -130,32 +130,32 @@ class NetworkPriorityClassifier {
return true;
}
- if (networkPriority instanceof VcnWifiUnderlyingNetworkPriority) {
+ if (networkPriority instanceof VcnWifiUnderlyingNetworkTemplate) {
return checkMatchesWifiPriorityRule(
- (VcnWifiUnderlyingNetworkPriority) networkPriority,
+ (VcnWifiUnderlyingNetworkTemplate) networkPriority,
networkRecord,
currentlySelected,
carrierConfig);
}
- if (networkPriority instanceof VcnCellUnderlyingNetworkPriority) {
+ if (networkPriority instanceof VcnCellUnderlyingNetworkTemplate) {
return checkMatchesCellPriorityRule(
vcnContext,
- (VcnCellUnderlyingNetworkPriority) networkPriority,
+ (VcnCellUnderlyingNetworkTemplate) networkPriority,
networkRecord,
subscriptionGroup,
snapshot);
}
logWtf(
- "Got unknown VcnUnderlyingNetworkPriority class: "
+ "Got unknown VcnUnderlyingNetworkTemplate class: "
+ networkPriority.getClass().getSimpleName());
return false;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesWifiPriorityRule(
- VcnWifiUnderlyingNetworkPriority networkPriority,
+ VcnWifiUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
@@ -202,7 +202,7 @@ class NetworkPriorityClassifier {
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static boolean checkMatchesCellPriorityRule(
VcnContext vcnContext,
- VcnCellUnderlyingNetworkPriority networkPriority,
+ VcnCellUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index cd124ee6a9fa..ca2e449ffc25 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -33,7 +33,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfig;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -498,10 +498,10 @@ public class UnderlyingNetworkController {
pw.println(
"Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
- pw.println("VcnUnderlyingNetworkPriority list:");
+ pw.println("VcnUnderlyingNetworkTemplate list:");
pw.increaseIndent();
int index = 0;
- for (VcnUnderlyingNetworkPriority priority :
+ for (VcnUnderlyingNetworkTemplate priority :
mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
pw.println("Priority index: " + index);
priority.dump(pw);
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 27ba854ab197..df2f0d58565e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -21,7 +21,7 @@ import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.vcn.VcnUnderlyingNetworkPriority;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -77,7 +77,7 @@ public class UnderlyingNetworkRecord {
static Comparator<UnderlyingNetworkRecord> getComparator(
VcnContext vcnContext,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -133,7 +133,7 @@ public class UnderlyingNetworkRecord {
void dump(
VcnContext vcnContext,
IndentingPrintWriter pw,
- LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 49a51d528422..f0e8b8f54e50 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -405,7 +405,7 @@ public class DisplayPolicy {
WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
? getStatusBar() : getNavigationBar();
if (targetBar != null) {
- requestTransientBars(targetBar);
+ requestTransientBars(targetBar, true /* isGestureOnSystemBar */);
}
}
break;
@@ -448,26 +448,25 @@ public class DisplayPolicy {
// TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
+
@Override
public void onSwipeFromTop() {
synchronized (mLock) {
- if (mStatusBar != null) {
- requestTransientBars(mStatusBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_TOP,
- false /* allowForAllPositions */);
+ final WindowState bar = mStatusBar != null
+ ? mStatusBar
+ : findAltBarMatchingPosition(ALT_BAR_TOP);
+ requestTransientBars(bar, true /* isGestureOnSystemBar */);
}
}
@Override
public void onSwipeFromBottom() {
synchronized (mLock) {
- if (mNavigationBar != null
- && mNavigationBarPosition == NAV_BAR_BOTTOM) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM,
- false /* allowForAllPositions */);
+ final WindowState bar = mNavigationBar != null
+ && mNavigationBarPosition == NAV_BAR_BOTTOM
+ ? mNavigationBar
+ : findAltBarMatchingPosition(ALT_BAR_BOTTOM);
+ requestTransientBars(bar, true /* isGestureOnSystemBar */);
}
}
@@ -477,13 +476,8 @@ public class DisplayPolicy {
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
- !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT
- || allowSideSwipe)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT, allowSideSwipe);
+ requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_RIGHT,
+ ALT_BAR_RIGHT);
}
excludedRegion.recycle();
}
@@ -494,17 +488,33 @@ public class DisplayPolicy {
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
- final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
- !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
- if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT
- || allowSideSwipe)) {
- requestTransientBars(mNavigationBar);
- }
- checkAltBarSwipeForTransientBars(ALT_BAR_LEFT, allowSideSwipe);
+ requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_LEFT,
+ ALT_BAR_LEFT);
}
excludedRegion.recycle();
}
+ private void requestTransientBarsForSideSwipe(Region excludedRegion,
+ int navBarSide, int altBarSide) {
+ final WindowState barMatchingSide = mNavigationBar != null
+ && mNavigationBarPosition == navBarSide
+ ? mNavigationBar
+ : findAltBarMatchingPosition(altBarSide);
+ final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
+ !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
+ if (barMatchingSide == null && !allowSideSwipe) {
+ return;
+ }
+
+ // Request transient bars on the matching bar, or any bar if we always allow
+ // side swipes to show the bars
+ final boolean isGestureOnSystemBar = barMatchingSide != null;
+ final WindowState bar = barMatchingSide != null
+ ? barMatchingSide
+ : findTransientNavOrAltBar();
+ requestTransientBars(bar, isGestureOnSystemBar);
+ }
+
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
@@ -654,21 +664,39 @@ public class DisplayPolicy {
mHandler.post(mGestureNavigationSettingsObserver::register);
}
- private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos,
- boolean allowForAllPositions) {
- if (mStatusBarAlt != null && (mStatusBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mStatusBarAlt);
+ /**
+ * Returns the first non-null alt bar window matching the given position.
+ */
+ private WindowState findAltBarMatchingPosition(@WindowManagerPolicy.AltBarPosition int pos) {
+ if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
+ return mStatusBarAlt;
}
- if (mNavigationBarAlt != null
- && (mNavigationBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mNavigationBarAlt);
+ if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
+ return mNavigationBarAlt;
}
- if (mClimateBarAlt != null && (mClimateBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mClimateBarAlt);
+ if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
+ return mClimateBarAlt;
}
- if (mExtraNavBarAlt != null && (mExtraNavBarAltPosition == pos || allowForAllPositions)) {
- requestTransientBars(mExtraNavBarAlt);
+ if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
+ return mExtraNavBarAlt;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the first non-null nav bar to request transient for.
+ */
+ private WindowState findTransientNavOrAltBar() {
+ if (mNavigationBar != null) {
+ return mNavigationBar;
+ }
+ if (mNavigationBarAlt != null) {
+ return mNavigationBarAlt;
+ }
+ if (mExtraNavBarAlt != null) {
+ return mExtraNavBarAlt;
}
+ return null;
}
void systemReady() {
@@ -2170,8 +2198,8 @@ public class DisplayPolicy {
updateSystemBarAttributes();
}
- private void requestTransientBars(WindowState swipeTarget) {
- if (!mService.mPolicy.isUserSetupComplete()) {
+ private void requestTransientBars(WindowState swipeTarget, boolean isGestureOnSystemBar) {
+ if (swipeTarget == null || !mService.mPolicy.isUserSetupComplete()) {
// Swipe-up for navigation bar is disabled during setup
return;
}
@@ -2207,7 +2235,8 @@ public class DisplayPolicy {
if (controlTarget.canShowTransient()) {
// Show transient bars if they are hidden; restore position if they are visible.
- mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE,
+ isGestureOnSystemBar);
controlTarget.showInsets(restorePositionTypes, false);
} else {
// Restore visibilities and positions of system bars.
@@ -2423,7 +2452,8 @@ public class DisplayPolicy {
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
if (!isNavBarEmpty(disableFlags)) {
- mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
+ mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC,
+ true /* isGestureOnSystemBar */);
}
}
@@ -2821,6 +2851,7 @@ public class DisplayPolicy {
void release() {
mHandler.post(mGestureNavigationSettingsObserver::unregister);
+ mImmersiveModeConfirmation.release();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index f3b9cdfd39e0..93bdf16a99ea 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -149,6 +149,11 @@ public class ImmersiveModeConfirmation {
}
}
+ void release() {
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.HIDE);
+ }
+
boolean onSettingChanged(int currentUserId) {
final boolean changed = loadSetting(currentUserId, mContext);
// Remove the window if the setting changes to be confirmed.
@@ -204,7 +209,12 @@ public class ImmersiveModeConfirmation {
if (mClingWindow != null) {
if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
// We don't care which root display area the window manager is specifying for removal.
- getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
+ try {
+ getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
+ return;
+ }
mClingWindow = null;
}
}
@@ -432,7 +442,11 @@ public class ImmersiveModeConfirmation {
// show the confirmation
WindowManager.LayoutParams lp = getClingWindowLayoutParams();
- getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ try {
+ getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
+ }
}
private final Runnable mConfirm = new Runnable() {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index dff7ff9931d7..9326a2ebb331 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -160,7 +160,7 @@ class InsetsPolicy {
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
- void showTransient(@InternalInsetsType int[] types) {
+ void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) {
boolean changed = false;
for (int i = types.length - 1; i >= 0; i--) {
final @InternalInsetsType int type = types[i];
@@ -177,8 +177,8 @@ class InsetsPolicy {
StatusBarManagerInternal statusBarManagerInternal =
mPolicy.getStatusBarManagerInternal();
if (statusBarManagerInternal != null) {
- statusBarManagerInternal.showTransient(
- mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(),
+ mShowingTransientTypes.toArray(), isGestureOnSystemBar);
}
updateBarControlTarget(mFocusedWin);
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index e33c4403a98b..1a1101e45f45 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -187,7 +187,6 @@ class InsetsStateController {
// Navigation bar doesn't get influenced by anything else
if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
- state.removeSource(ITYPE_IME);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_CLIMATE_BAR);
state.removeSource(ITYPE_CAPTION_BAR);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 535bbb74d1e3..3f2e97505765 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2051,19 +2051,25 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
try {
final Task task = r.getTask();
- final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
- // This will change the root pinned task's windowing mode to its original mode, ensuring
- // we only have one root task that is in pinned mode.
+ // If the activity in current PIP task needs to be moved back to the parent Task of next
+ // PIP activity, we can't use that parent Task as the next PIP Task.
+ // Because we need to start the Shell transition from the root Task, we delay to dismiss
+ // the current PIP task until root Task is ready.
+ boolean origPipWillBeMovedToTask = false;
+ final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
if (rootPinnedTask != null) {
- rootPinnedTask.dismissPip();
+ final ActivityRecord topPipActivity = rootPinnedTask.getTopMostActivity();
+ if (topPipActivity != null && topPipActivity.getLastParentBeforePip() == task) {
+ origPipWillBeMovedToTask = true;
+ }
}
// Set a transition to ensure that we don't immediately try and update the visibility
// of the activity entering PIP
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
- final boolean singleActivity = task.getChildCount() == 1;
+ final boolean singleActivity = task.getChildCount() == 1 && !origPipWillBeMovedToTask;
final Task rootTask;
if (singleActivity) {
rootTask = task;
@@ -2117,7 +2123,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final ActivityRecord oldTopActivity = task.getTopMostActivity();
if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
&& task.getDisplayContent().mAppTransition.containsTransitRequest(
- TRANSIT_TO_BACK)) {
+ TRANSIT_TO_BACK) && !origPipWillBeMovedToTask) {
task.getDisplayContent().mClosingApps.add(oldTopActivity);
oldTopActivity.mRequestForceTransition = true;
}
@@ -2133,6 +2139,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
+ // This will change the root pinned task's windowing mode to its original mode, ensuring
+ // we only have one root task that is in pinned mode.
+ if (rootPinnedTask != null) {
+ rootTask.mTransitionController.collect(rootPinnedTask);
+ rootPinnedTask.dismissPip();
+ }
+
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 659c7713a613..7617726e1fcd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -478,7 +478,6 @@ class Task extends TaskFragment {
// to layout without loading all the task snapshots
final PersistedTaskSnapshotData mLastTaskSnapshotData;
- private Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
/** @see #setCanAffectSystemUiFlags */
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8299ceae57fd..d133ca96b45e 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -161,6 +161,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
*/
int mMinHeight;
+ Dimmer mDimmer = new Dimmer(this);
+
/** This task fragment will be removed when the cleanup of its children are done. */
private boolean mIsRemovalRequested;
@@ -2349,6 +2351,34 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
@Override
+ Dimmer getDimmer() {
+ // If the window is in an embedded TaskFragment, we want to dim at the TaskFragment.
+ if (asTask() == null) {
+ return mDimmer;
+ }
+
+ return super.getDimmer();
+ }
+
+ @Override
+ void prepareSurfaces() {
+ if (asTask() != null) {
+ super.prepareSurfaces();
+ return;
+ }
+
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+
+ // Bounds need to be relative, as the dim layer is a child.
+ final Rect dimBounds = getBounds();
+ dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
+ if (mDimmer.updateDims(getPendingTransaction(), dimBounds)) {
+ scheduleAnimation();
+ }
+ }
+
+ @Override
boolean canBeAnimationTarget() {
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index df8953c318dc..db8da1146f1d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManager.UserRestrictionSource;
import android.os.storage.StorageManager;
import android.permission.AdminPermissionControlParams;
import android.permission.IPermissionManager;
@@ -286,6 +287,7 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
@@ -13271,14 +13273,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
List<UserManager.EnforcingUser> sources = mUserManager
.getUserRestrictionSources(restriction, UserHandle.of(userId));
- if (sources == null || sources.isEmpty()) {
+ if (sources == null) {
// The restriction is not enforced.
return null;
- } else if (sources.size() > 1) {
+ }
+ int sizeBefore = sources.size();
+ if (sizeBefore > 1) {
+ Slogf.d(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): "
+ + "%d sources found, excluding those set by UserManager",
+ userId, restriction, sizeBefore);
+ sources = getDevicePolicySources(sources);
+ }
+ if (sources.isEmpty()) {
+ // The restriction is not enforced (or is just enforced by the system)
+ return null;
+ }
+
+ if (sources.size() > 1) {
// In this case, we'll show an admin support dialog that does not
// specify the admin.
// TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
// the admin for the calling user.
+ Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
+ + "sources for restriction %s on user %d", restriction, userId);
result = new Bundle();
result.putInt(Intent.EXTRA_USER_ID, userId);
return result;
@@ -13324,6 +13341,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
+ * Excludes restrictions imposed by UserManager.
+ */
+ private List<UserManager.EnforcingUser> getDevicePolicySources(
+ List<UserManager.EnforcingUser> sources) {
+ int sizeBefore = sources.size();
+ List<UserManager.EnforcingUser> realSources = new ArrayList<>(sizeBefore);
+ for (int i = 0; i < sizeBefore; i++) {
+ UserManager.EnforcingUser source = sources.get(i);
+ int type = source.getUserRestrictionSource();
+ if (type != UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
+ && type != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+ // TODO(b/128928355): add unit test
+ Slogf.d(LOG_TAG, "excluding source of type %s at index %d",
+ userRestrictionSourceToString(type), i);
+ continue;
+ }
+ realSources.add(source);
+ }
+ return realSources;
+ }
+
+ private static String userRestrictionSourceToString(@UserRestrictionSource int source) {
+ return DebugUtils.flagsToString(UserManager.class, "RESTRICTION_", source);
+ }
+
+ /**
* @param restriction The restriction enforced by admin. It could be any user restriction or
* policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
* {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 44a8b3001a71..04a6eeecb320 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -74,6 +74,7 @@ import com.android.server.testutils.mock
import com.android.server.testutils.nullable
import com.android.server.testutils.whenever
import com.android.server.utils.WatchedArrayMap
+import libcore.util.HexEncoding
import org.junit.Assert
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -140,6 +141,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
.mockStatic(EventLog::class.java)
.mockStatic(LocalServices::class.java)
.mockStatic(DeviceConfig::class.java)
+ .mockStatic(HexEncoding::class.java)
.apply(withSession)
session = apply.startMocking()
whenever(mocks.settings.insertPackageSettingLPw(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
new file mode 100644
index 000000000000..6de12cb98719
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.SharedLibraryInfo
+import android.content.pm.VersionedPackage
+import android.os.Build
+import android.os.storage.StorageManager
+import android.util.ArrayMap
+import android.util.PackageUtils
+import com.android.server.SystemConfig.SharedLibraryEntry
+import com.android.server.compat.PlatformCompat
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.testutils.any
+import com.android.server.testutils.eq
+import com.android.server.testutils.mock
+import com.android.server.testutils.nullable
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+import com.android.server.utils.WatchedLongSparseArray
+import com.google.common.truth.Truth.assertThat
+import libcore.util.HexEncoding
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.io.File
+import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(JUnit4::class)
+class SharedLibrariesImplTest {
+
+ companion object {
+ const val TEST_LIB_NAME = "test.lib"
+ const val TEST_LIB_PACKAGE_NAME = "com.android.lib.test"
+ const val BUILTIN_LIB_NAME = "builtin.lib"
+ const val STATIC_LIB_NAME = "static.lib"
+ const val STATIC_LIB_VERSION = 7L
+ const val STATIC_LIB_PACKAGE_NAME = "com.android.lib.static.provider"
+ const val DYNAMIC_LIB_NAME = "dynamic.lib"
+ const val DYNAMIC_LIB_PACKAGE_NAME = "com.android.lib.dynamic.provider"
+ const val CONSUMER_PACKAGE_NAME = "com.android.lib.consumer"
+ const val VERSION_UNDEFINED = SharedLibraryInfo.VERSION_UNDEFINED.toLong()
+ }
+
+ @Rule
+ @JvmField
+ val mRule = MockSystemRule()
+
+ private val mExistingPackages: ArrayMap<String, AndroidPackage> = ArrayMap()
+ private val mExistingSettings: MutableMap<String, PackageSetting> = mutableMapOf()
+
+ private lateinit var mSharedLibrariesImpl: SharedLibrariesImpl
+ private lateinit var mPms: PackageManagerService
+ private lateinit var mSettings: Settings
+
+ @Mock
+ private lateinit var mDeletePackageHelper: DeletePackageHelper
+ @Mock
+ private lateinit var mStorageManager: StorageManager
+ @Mock
+ private lateinit var mFile: File
+ @Mock
+ private lateinit var mPlatformCompat: PlatformCompat
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mRule.system().stageNominalSystemState()
+ addExistingPackages()
+
+ val testParams = PackageManagerServiceTestParams().apply {
+ packages = mExistingPackages
+ }
+ mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
+ mSettings = mRule.mocks().injector.settings
+ mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
+ mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
+ addExistingSharedLibraries()
+
+ whenever(mSettings.getPackageLPr(any())) { mExistingSettings[arguments[0]] }
+ whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
+ .thenReturn(mStorageManager)
+ whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
+ doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
+ whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
+ .thenReturn(PackageManager.DELETE_SUCCEEDED)
+ whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
+ wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
+ .thenReturn(PackageUtils.computeSha256DigestBytes(
+ mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ .pkg.signingDetails.signatures!![0].toByteArray()))
+ }
+
+ @Test
+ fun snapshot_shouldSealed() {
+ val builtinLibs = mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME]
+ assertThat(builtinLibs).isNotNull()
+
+ assertFailsWith(IllegalStateException::class) {
+ mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME] = WatchedLongSparseArray()
+ }
+ assertFailsWith(IllegalStateException::class) {
+ builtinLibs!!.put(VERSION_UNDEFINED, libOfBuiltin(BUILTIN_LIB_NAME))
+ }
+ }
+
+ @Test
+ fun addBuiltInSharedLibrary() {
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(TEST_LIB_NAME))
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME)).isNotNull()
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(TEST_LIB_NAME, VERSION_UNDEFINED))
+ .isNotNull()
+ }
+
+ @Test
+ fun addBuiltInSharedLibrary_withDuplicateLibName() {
+ val duplicate = libEntry(BUILTIN_LIB_NAME, "duplicate.path")
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(duplicate)
+ val sharedLibInfo = mSharedLibrariesImpl
+ .getSharedLibraryInfo(BUILTIN_LIB_NAME, VERSION_UNDEFINED)
+
+ assertThat(sharedLibInfo).isNotNull()
+ assertThat(sharedLibInfo!!.path).isNotEqualTo(duplicate.filename)
+ }
+
+ @Test
+ fun commitSharedLibraryInfo_withStaticSharedLib() {
+ val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(testInfo)
+ val sharedLibInfos = mSharedLibrariesImpl
+ .getStaticLibraryInfos(testInfo.declaringPackage.packageName)
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME))
+ .isNotNull()
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(testInfo.name, testInfo.longVersion))
+ .isNotNull()
+ assertThat(sharedLibInfos).isNotNull()
+ assertThat(sharedLibInfos.get(testInfo.longVersion)).isNotNull()
+ }
+
+ @Test
+ fun removeSharedLibrary() {
+ doAnswer { mutableListOf(VersionedPackage(CONSUMER_PACKAGE_NAME, 1L)) }.`when`(mPms)
+ .getPackagesUsingSharedLibrary(any(), any(), any(), any())
+ val staticInfo = mSharedLibrariesImpl
+ .getSharedLibraryInfo(STATIC_LIB_NAME, STATIC_LIB_VERSION)!!
+
+ mSharedLibrariesImpl.removeSharedLibraryLPw(STATIC_LIB_NAME, STATIC_LIB_VERSION)
+
+ assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(STATIC_LIB_NAME)).isNull()
+ assertThat(mSharedLibrariesImpl
+ .getStaticLibraryInfos(staticInfo.declaringPackage.packageName)).isNull()
+ verify(mExistingSettings[CONSUMER_PACKAGE_NAME]!!)
+ .setOverlayPathsForLibrary(any(), nullable(), any())
+ }
+
+ @Test
+ fun pruneUnusedStaticSharedLibraries() {
+ mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE, 0)
+
+ verify(mDeletePackageHelper)
+ .deletePackageX(eq(STATIC_LIB_PACKAGE_NAME), any(), any(), any(), any())
+ }
+
+ @Test
+ fun getLatestSharedLibraVersion() {
+ val newLibSetting = addPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+
+ val latestInfo = mSharedLibrariesImpl.getLatestSharedLibraVersionLPr(newLibSetting.pkg)!!
+
+ assertThat(latestInfo).isNotNull()
+ assertThat(latestInfo.name).isEqualTo(STATIC_LIB_NAME)
+ assertThat(latestInfo.longVersion).isEqualTo(STATIC_LIB_VERSION)
+ }
+
+ @Test
+ fun getStaticSharedLibLatestVersionSetting() {
+ val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
+ val parsedPackage = pair.second as ParsedPackage
+ val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ null, null, null, 0, 0, false, null, null)
+ val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
+
+ val latestInfoSetting =
+ mSharedLibrariesImpl.getStaticSharedLibLatestVersionSetting(scanResult)!!
+
+ assertThat(latestInfoSetting).isNotNull()
+ assertThat(latestInfoSetting.packageName).isEqualTo(STATIC_LIB_PACKAGE_NAME)
+ }
+
+ @Test
+ fun updateSharedLibraries_withDynamicLibPackage() {
+ val testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ }
+
+ @Test
+ fun updateSharedLibraries_withStaticLibPackage() {
+ val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun updateSharedLibraries_withConsumerPackage() {
+ val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
+
+ mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
+ null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
+
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun updateAllSharedLibraries() {
+ mExistingSettings.forEach {
+ assertThat(it.value.usesLibraryFiles).isEmpty()
+ }
+
+ mSharedLibrariesImpl.updateAllSharedLibrariesLPw(
+ null /* updatedPkg */, null /* updatedPkgSetting */, mExistingPackages)
+
+ var testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+
+ testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+
+ testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
+ assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
+ assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
+ assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
+ }
+
+ @Test
+ fun getAllowedSharedLibInfos_withStaticSharedLibInfo() {
+ val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
+ val scanResult = ScanResult(mock(), true, null, null,
+ false, 0, null, testInfo, null)
+
+ val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+ assertThat(allowedInfos).hasSize(1)
+ assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+ }
+
+ @Test
+ fun getAllowedSharedLibInfos_withDynamicSharedLibInfo() {
+ val testInfo = libOfDynamic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME)
+ val pair = createBasicAndroidPackage(
+ TEST_LIB_PACKAGE_NAME, 10L, libraries = arrayOf(TEST_LIB_NAME))
+ val parsedPackage = pair.second.apply {
+ isSystem = true
+ } as ParsedPackage
+ val packageSetting = mRule.system()
+ .createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal())
+ .setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build()
+ val scanRequest = ScanRequest(parsedPackage, null, null, null,
+ null, null, null, 0, 0, false, null, null)
+ val scanResult = ScanResult(scanRequest, true, packageSetting, null,
+ false, 0, null, null, listOf(testInfo))
+
+ val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult)
+
+ assertThat(allowedInfos).hasSize(1)
+ assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME)
+ }
+
+ private fun addExistingPackages() {
+ // add a dynamic shared library that is using the builtin library
+ addPackage(DYNAMIC_LIB_PACKAGE_NAME, 1L,
+ libraries = arrayOf(DYNAMIC_LIB_NAME),
+ usesLibraries = arrayOf(BUILTIN_LIB_NAME))
+
+ // add a static shared library v7 that is using the dynamic shared library
+ addPackage(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_VERSION,
+ staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = STATIC_LIB_VERSION,
+ usesLibraries = arrayOf(DYNAMIC_LIB_NAME))
+
+ // add a consumer package that is using the dynamic and static shared library
+ addPackage(CONSUMER_PACKAGE_NAME, 1L,
+ usesLibraries = arrayOf(DYNAMIC_LIB_NAME),
+ usesStaticLibraries = arrayOf(STATIC_LIB_NAME),
+ usesStaticLibraryVersions = arrayOf(STATIC_LIB_VERSION))
+ }
+
+ private fun addExistingSharedLibraries() {
+ mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(BUILTIN_LIB_NAME))
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+ libOfDynamic(DYNAMIC_LIB_PACKAGE_NAME, DYNAMIC_LIB_NAME))
+ mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
+ libOfStatic(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_NAME, STATIC_LIB_VERSION))
+ }
+
+ private fun addPackage(
+ packageName: String,
+ version: Long,
+ libraries: Array<String>? = null,
+ staticLibrary: String? = null,
+ staticLibraryVersion: Long = 0L,
+ usesLibraries: Array<String>? = null,
+ usesStaticLibraries: Array<String>? = null,
+ usesStaticLibraryVersions: Array<Long>? = null
+ ): PackageSetting {
+ val pair = createBasicAndroidPackage(packageName, version, libraries, staticLibrary,
+ staticLibraryVersion, usesLibraries, usesStaticLibraries, usesStaticLibraryVersions)
+ val apkPath = pair.first
+ val parsingPackage = pair.second
+ val spyPkg = spy((parsingPackage as ParsedPackage).hideAsFinal())
+ mExistingPackages[packageName] = spyPkg
+
+ val spyPackageSetting = spy(mRule.system()
+ .createBasicSettingBuilder(apkPath.parentFile, spyPkg).build())
+ mExistingSettings[spyPackageSetting.packageName] = spyPackageSetting
+
+ return spyPackageSetting
+ }
+
+ private fun createBasicAndroidPackage(
+ packageName: String,
+ version: Long,
+ libraries: Array<String>? = null,
+ staticLibrary: String? = null,
+ staticLibraryVersion: Long = 0L,
+ usesLibraries: Array<String>? = null,
+ usesStaticLibraries: Array<String>? = null,
+ usesStaticLibraryVersions: Array<Long>? = null
+ ): Pair<File, PackageImpl> {
+ assertFalse { libraries != null && staticLibrary != null }
+ assertTrue { (usesStaticLibraries?.size ?: -1) == (usesStaticLibraryVersions?.size ?: -1) }
+
+ val pair = mRule.system()
+ .createBasicAndroidPackage(mRule.system().dataAppDirectory, packageName, version)
+ pair.second.apply {
+ setTargetSdkVersion(Build.VERSION_CODES.S)
+ libraries?.forEach { addLibraryName(it) }
+ staticLibrary?.let {
+ setStaticSharedLibName(it)
+ setStaticSharedLibVersion(staticLibraryVersion)
+ setStaticSharedLibrary(true)
+ }
+ usesLibraries?.forEach { addUsesLibrary(it) }
+ usesStaticLibraries?.forEachIndexed { index, s ->
+ addUsesStaticLibrary(s,
+ usesStaticLibraryVersions?.get(index) ?: 0L,
+ arrayOf(s))
+ }
+ }
+ return pair
+ }
+
+ private fun libEntry(libName: String, path: String? = null): SharedLibraryEntry =
+ SharedLibraryEntry(libName, path ?: builtinLibPath(libName),
+ arrayOfNulls(0), false /* isNative */)
+
+ private fun libOfBuiltin(libName: String): SharedLibraryInfo =
+ SharedLibraryInfo(builtinLibPath(libName),
+ null /* packageName */,
+ null /* codePaths */,
+ libName,
+ VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ VersionedPackage(PLATFORM_PACKAGE_NAME, 0L /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun libOfStatic(
+ packageName: String,
+ libName: String,
+ version: Long
+ ): SharedLibraryInfo =
+ SharedLibraryInfo(null /* path */,
+ packageName,
+ listOf(apkPath(packageName)),
+ libName,
+ version,
+ SharedLibraryInfo.TYPE_STATIC,
+ VersionedPackage(packageName, version /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun libOfDynamic(packageName: String, libName: String): SharedLibraryInfo =
+ SharedLibraryInfo(null /* path */,
+ packageName,
+ listOf(apkPath(packageName)),
+ libName,
+ VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_DYNAMIC,
+ VersionedPackage(packageName, 1L /* versionCode */),
+ null /* dependentPackages */,
+ null /* dependencies */,
+ false /* isNative */)
+
+ private fun builtinLibPath(libName: String): String = "/system/app/$libName/$libName.jar"
+
+ private fun apkPath(packageName: String): String =
+ File(mRule.system().dataAppDirectory, packageName).path
+}
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 d18030f10603..97ebdd4ad7fa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -595,6 +595,20 @@ public class AbstractAccessibilityServiceConnectionTest {
}
@Test
+ public void getCurrentMagnificationRegion_returnRegion() {
+ final int displayId = 1;
+ final Region region = new Region(10, 20, 100, 200);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockMagnificationProcessor).getCurrentMagnificationRegion(eq(displayId), any(),
+ anyBoolean());
+
+ final Region result = mServiceConnection.getCurrentMagnificationRegion(displayId);
+ assertEquals(result, region);
+ }
+
+ @Test
public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() {
final int displayId = 1;
final float centerX = 480.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 3ade9ff61735..953b5368c86f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -343,6 +343,20 @@ public class AccessibilityManagerServiceTest {
eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), ArgumentMatchers.isNotNull());
}
+ @Test
+ public void testFollowTypingEnabled_defaultEnabledAndThenDisable_propagateToController() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
+ 0, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readMagnificationFollowTypingLocked(userState);
+
+ verify(mMockMagnificationController).setMagnificationFollowTypingEnabled(false);
+ }
+
@SmallTest
@Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b9d94edc5981..27637c2ba5d5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -172,6 +172,7 @@ public class AccessibilityUserStateTest {
mUserState.getMagnificationModeLocked(TEST_DISPLAY));
assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
+ assertTrue(mUserState.isMagnificationFollowTypingEnabled());
}
@Test
@@ -374,6 +375,15 @@ public class AccessibilityUserStateTest {
}
@Test
+ public void setMagnificationFollowTypingEnabled_defaultTrueAndThenDisable_returnFalse() {
+ assertTrue(mUserState.isMagnificationFollowTypingEnabled());
+
+ mUserState.setMagnificationFollowTypingEnabled(false);
+
+ assertFalse(mUserState.isMagnificationFollowTypingEnabled());
+ }
+
+ @Test
public void setFocusAppearanceData_returnExpectedFocusAppearanceData() {
final int focusStrokeWidthValue = 100;
final int focusColorValue = Color.BLUE;
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 99d6c2af6f75..c4040b405d19 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -184,6 +184,38 @@ public class MagnificationProcessorTest {
}
@Test
+ public void getCurrentMagnificationRegion_windowModeActivated_returnRegion() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ any());
+
+ final Region result = new Region();
+ mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+ result, /* canControlMagnification= */true);
+ assertEquals(region, result);
+ }
+
+ @Test
+ public void getCurrentMagnificationRegion_fullscreenModeActivated_returnRegion() {
+ final Region region = new Region(10, 20, 100, 200);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
+ doAnswer((invocation) -> {
+ ((Region) invocation.getArguments()[1]).set(region);
+ return null;
+ }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
+ any());
+
+ final Region result = new Region();
+ mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY,
+ result, /* canControlMagnification= */true);
+ assertEquals(region, result);
+ }
+
+ @Test
public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_FULLSCREEN)
@@ -222,7 +254,7 @@ public class MagnificationProcessorTest {
}
@Test
- public void reset_fullscreenMagnificationActivated() {
+ public void resetFullscreenMagnification_fullscreenMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
@@ -231,7 +263,7 @@ public class MagnificationProcessorTest {
}
@Test
- public void reset_windowMagnificationActivated() {
+ public void resetCurrentMagnification_windowMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 96af61737bff..a9b7cfb5e338 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+
import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import static org.junit.Assert.assertEquals;
@@ -23,10 +25,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import android.accessibilityservice.MagnificationConfig;
import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -105,6 +106,8 @@ public class FullScreenMagnificationControllerTest {
private final MagnificationScaleProvider mScaleProvider = mock(
MagnificationScaleProvider.class);
+ private final ArgumentCaptor<MagnificationConfig> mConfigCaptor = ArgumentCaptor.forClass(
+ MagnificationConfig.class);
ValueAnimator mMockValueAnimator;
ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
@@ -142,13 +145,13 @@ public class FullScreenMagnificationControllerTest {
register(DISPLAY_1);
register(INVALID_DISPLAY);
verify(mMockContext).registerReceiver(
- (BroadcastReceiver) anyObject(), (IntentFilter) anyObject());
+ any(BroadcastReceiver.class), any(IntentFilter.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(DISPLAY_0), (MagnificationCallbacks) anyObject());
+ eq(DISPLAY_0), any(MagnificationCallbacks.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
+ eq(DISPLAY_1), any(MagnificationCallbacks.class));
verify(mMockWindowManager).setMagnificationCallbacks(
- eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
+ eq(INVALID_DISPLAY), any(MagnificationCallbacks.class));
assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertTrue(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
assertFalse(mFullScreenMagnificationController.isRegistered(INVALID_DISPLAY));
@@ -159,9 +162,9 @@ public class FullScreenMagnificationControllerTest {
register(DISPLAY_0);
register(DISPLAY_1);
mFullScreenMagnificationController.unregister(DISPLAY_0);
- verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
+ verify(mMockContext, times(0)).unregisterReceiver(any(BroadcastReceiver.class));
mFullScreenMagnificationController.unregister(DISPLAY_1);
- verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
+ verify(mMockContext).unregisterReceiver(any(BroadcastReceiver.class));
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
@@ -343,6 +346,7 @@ public class FullScreenMagnificationControllerTest {
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+ final MagnificationConfig config = buildConfig(scale, newCenter.x, newCenter.y);
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
@@ -353,8 +357,9 @@ public class FullScreenMagnificationControllerTest {
assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
assertEquals(newCenter.y, mFullScreenMagnificationController.getCenterY(displayId), 0.5);
assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
verify(mMockValueAnimator).start();
verify(mRequestObserver).onRequestMagnificationSpec(displayId, SERVICE_ID_1);
@@ -494,8 +499,11 @@ public class FullScreenMagnificationControllerTest {
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
callbacks.onMagnificationRegionChanged(OTHER_REGION);
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f,
- OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY());
+ MagnificationConfig config = buildConfig(1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(),
+ OTHER_MAGNIFICATION_BOUNDS.centerY());
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(OTHER_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
}
@Test
@@ -650,7 +658,7 @@ public class FullScreenMagnificationControllerTest {
reset(mMockAms);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
verify(mMockAms).notifyMagnificationChanged(eq(displayId),
- eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
+ eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
}
@@ -668,8 +676,8 @@ public class FullScreenMagnificationControllerTest {
assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
- any(Region.class), anyFloat(), anyFloat(), anyFloat());
+ verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class),
+ any(MagnificationConfig.class));
verify(mAnimationCallback).onResult(true);
}
@@ -726,7 +734,7 @@ public class FullScreenMagnificationControllerTest {
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext).registerReceiver(
- broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
+ broadcastReceiverCaptor.capture(), any(IntentFilter.class));
BroadcastReceiver br = broadcastReceiverCaptor.getValue();
zoomIn2xToMiddle(DISPLAY_0);
zoomIn2xToMiddle(DISPLAY_1);
@@ -913,6 +921,22 @@ public class FullScreenMagnificationControllerTest {
}
@Test
+ public void requestRectOnScreen_disabledByPrefSetting_doesNothing() {
+ register(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_0);
+ Mockito.reset(mMockWindowManager);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(DISPLAY_0);
+ MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
+ mFullScreenMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+ mFullScreenMagnificationController.onRectangleOnScreenRequested(DISPLAY_0, 0, 0, 1, 1);
+
+ assertThat(getCurrentMagnificationSpec(DISPLAY_0), closeTo(startSpec));
+ verify(mMockWindowManager, never()).setMagnificationSpec(eq(DISPLAY_0),
+ argThat(closeTo(expectedEndSpec)));
+ }
+
+ @Test
public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
@@ -1031,6 +1055,7 @@ public class FullScreenMagnificationControllerTest {
MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+ final MagnificationConfig config = buildConfig(scale, firstCenter.x, firstCenter.y);
MagnificationSpec firstEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
@@ -1047,8 +1072,9 @@ public class FullScreenMagnificationControllerTest {
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y);
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION),
+ mConfigCaptor.capture());
+ assertConfigEquals(config, mConfigCaptor.getValue());
Mockito.reset(mMockWindowManager);
// Intermediate point
@@ -1062,6 +1088,7 @@ public class FullScreenMagnificationControllerTest {
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
+ final MagnificationConfig newConfig = buildConfig(scale, newCenter.x, newCenter.y);
MagnificationSpec newEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
assertTrue(mFullScreenMagnificationController.setCenter(displayId,
@@ -1070,8 +1097,9 @@ public class FullScreenMagnificationControllerTest {
// Animation should have been restarted
verify(mMockValueAnimator, times(2)).start();
- verify(mMockAms).notifyMagnificationChanged(displayId,
- INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
+ verify(mMockAms, times(2)).notifyMagnificationChanged(eq(displayId),
+ eq(INITIAL_MAGNIFICATION_REGION), mConfigCaptor.capture());
+ assertConfigEquals(newConfig, mConfigCaptor.getValue());
// New starting point should be where we left off
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
@@ -1155,7 +1183,7 @@ public class FullScreenMagnificationControllerTest {
Region regionArg = (Region) args[1];
regionArg.set(INITIAL_MAGNIFICATION_REGION);
return null;
- }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
+ }).when(mMockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
}
private void resetMockWindowManager() {
@@ -1201,6 +1229,19 @@ public class FullScreenMagnificationControllerTest {
magnifiedBounds.centerY() - scale * center.y);
}
+ private MagnificationConfig buildConfig(float scale, float centerX, float centerY) {
+ return new MagnificationConfig.Builder().setMode(
+ MAGNIFICATION_MODE_FULLSCREEN).setScale(scale).setCenterX(centerX).setCenterY(
+ centerY).build();
+ }
+
+ private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig result) {
+ assertEquals(expected.getMode(), result.getMode());
+ assertEquals(expected.getScale(), result.getScale(), 0f);
+ assertEquals(expected.getCenterX(), result.getCenterX(), 0f);
+ assertEquals(expected.getCenterY(), result.getCenterY(), 0f);
+ }
+
private MagnificationSpec getInterpolatedMagSpec(MagnificationSpec start, MagnificationSpec end,
float fraction) {
MagnificationSpec interpolatedSpec = new MagnificationSpec();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 0054fc328932..064b76243057 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -430,6 +430,21 @@ public class MagnificationControllerTest {
}
@Test
+ public void onSourceBoundsChanged_notifyMagnificationChanged() {
+ Rect rect = new Rect(0, 0, 100, 120);
+ Region region = new Region(rect);
+
+ mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, rect);
+
+ final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
+ MagnificationConfig.class);
+ verify(mService).notifyMagnificationChanged(eq(TEST_DISPLAY), eq(region),
+ configCaptor.capture());
+ assertEquals(rect.exactCenterX(), configCaptor.getValue().getCenterX(), 0);
+ assertEquals(rect.exactCenterY(), configCaptor.getValue().getCenterY(), 0);
+ }
+
+ @Test
public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -464,6 +479,14 @@ public class MagnificationControllerTest {
}
@Test
+ public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
+ mMagnificationController.setMagnificationFollowTypingEnabled(false);
+
+ verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+ verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
+ }
+
+ @Test
public void onRectangleOnScreenRequested_fullScreenIsActivated_fullScreenDispatchEvent() {
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
true);
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 a62c0d5e1eaf..8da513b50d65 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
@@ -274,6 +274,123 @@ public class WindowMagnificationManagerTest {
}
@Test
+ public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnification()
+ throws RemoteException {
+ final float distanceX = 10f;
+ final float distanceY = 10f;
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.inset(-10, -10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(500f), eq(500f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnification()
+ throws RemoteException {
+ final PointF initialPoint = new PointF(50f, 50f);
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f,
+ initialPoint.x, initialPoint.y);
+ mMockConnection.getConnectionCallback().onDrag(TEST_DISPLAY);
+ mWindowMagnificationManager.onImeWindowVisibilityChanged(true);
+ final Region outRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY),
+ eq(3f), eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region beforeRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
+
+ mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
new file mode 100644
index 000000000000..d4bac2c0402d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static android.testing.TestableLooper.RunWithLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class BiometricSchedulerOperationTest {
+
+ public interface FakeHal {}
+ public abstract static class InterruptableMonitor<T>
+ extends HalClientMonitor<T> implements Interruptable {
+ public InterruptableMonitor() {
+ super(null, null, null, null, 0, null, 0, 0, 0, 0, 0);
+ }
+ }
+
+ @Mock
+ private InterruptableMonitor<FakeHal> mClientMonitor;
+ @Mock
+ private BaseClientMonitor.Callback mClientCallback;
+ @Mock
+ private FakeHal mHal;
+ @Captor
+ ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+
+ private Handler mHandler;
+ private BiometricSchedulerOperation mOperation;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
+ mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
+ }
+
+ @Test
+ public void testStartWithCookie() {
+ final int cookie = 200;
+ when(mClientMonitor.getCookie()).thenReturn(cookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(cookie);
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), cookie);
+
+ assertThat(started).isTrue();
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ assertThat(mOperation.isStarted()).isTrue();
+ }
+
+ @Test
+ public void testNoStartWithoutCookie() {
+ final int goodCookie = 20;
+ final int badCookie = 22;
+ when(mClientMonitor.getCookie()).thenReturn(goodCookie);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
+ final boolean started = mOperation.startWithCookie(
+ mock(BaseClientMonitor.Callback.class), badCookie);
+
+ assertThat(started).isFalse();
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ }
+
+ @Test
+ public void startsWhenReadyAndHalAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+
+ assertThat(mOperation.isStarted()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+
+ verify(mClientCallback).onClientStarted(eq(mClientMonitor));
+ verify(cb).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback, never()).onClientFinished(any(), anyBoolean());
+ verify(cb, never()).onClientFinished(any(), anyBoolean());
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ }
+
+ @Test
+ public void startFailsWhenReadyButHalNotAvailable() {
+ when(mClientMonitor.getCookie()).thenReturn(0);
+ when(mClientMonitor.getFreshDaemon()).thenReturn(null);
+
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(cb);
+ verify(mClientMonitor, never()).start(any());
+
+ assertThat(mOperation.isStarted()).isFalse();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isTrue();
+
+ verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor));
+ verify(cb, never()).onClientStarted(eq(mClientMonitor));
+ verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false));
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(false));
+ }
+
+ @Test
+ public void doesNotStartWithCookie() {
+ when(mClientMonitor.getCookie()).thenReturn(9);
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotRestart() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void abortsNotRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.abort();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(mClientMonitor).unableToStart();
+ verify(mClientMonitor).destroy();
+ assertThrows(IllegalStateException.class,
+ () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ }
+
+ @Test
+ public void cannotAbortRunning() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+
+ assertThrows(IllegalStateException.class, () -> mOperation.abort());
+ }
+
+ @Test
+ public void cancel() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.start(startCb);
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ verify(mClientMonitor).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).destroy();
+
+ mStartCallback.getValue().onClientFinished(mClientMonitor, true);
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+
+ // should be unused since the operation was started
+ verify(cancelCb, never()).onClientStarted(any());
+ verify(cancelCb, never()).onClientFinished(any(), anyBoolean());
+ }
+
+ @Test
+ public void cancelWithoutStarting() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ mOperation.cancel(mHandler, cancelCb);
+
+ assertThat(mOperation.isCanceling()).isTrue();
+ ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
+ ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+ verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
+
+ cbCaptor.getValue().onClientFinished(mClientMonitor, true);
+ verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void markCanceling() {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.markCanceling();
+
+ assertThat(mOperation.isMarkedCanceling()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ assertThat(mOperation.isFinished()).isFalse();
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor, never()).destroy();
+ }
+
+ @Test
+ public void cancelPendingWithCookie() {
+ markCancellingAndStart(2);
+ }
+
+ @Test
+ public void cancelPendingWithoutCookie() {
+ markCancellingAndStart(null);
+ }
+
+ private void markCancellingAndStart(Integer withCookie) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+ if (withCookie != null) {
+ when(mClientMonitor.getCookie()).thenReturn(withCookie);
+ }
+
+ mOperation.markCanceling();
+ final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ if (withCookie != null) {
+ mOperation.startWithCookie(cb, withCookie);
+ } else {
+ mOperation.start(cb);
+ }
+
+ assertThat(mOperation.isFinished()).isTrue();
+ verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ verify(mClientMonitor, never()).start(any());
+ verify(mClientMonitor, never()).cancel();
+ verify(mClientMonitor, never()).cancelWithoutStarting(any());
+ verify(mClientMonitor, never()).unableToStart();
+ verify(mClientMonitor).destroy();
+ }
+
+ @Test
+ public void cancelWatchdogWhenStarted() {
+ cancelWatchdog(true);
+ }
+
+ @Test
+ public void cancelWatchdogWithoutStarting() {
+ cancelWatchdog(false);
+ }
+
+ private void cancelWatchdog(boolean start) {
+ when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
+
+ mOperation.start(mock(BaseClientMonitor.Callback.class));
+ if (start) {
+ verify(mClientMonitor).start(mStartCallback.capture());
+ mStartCallback.getValue().onClientStarted(mClientMonitor);
+ }
+ mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+
+ assertThat(mOperation.isCanceling()).isTrue();
+
+ // omit call to onClientFinished and trigger watchdog
+ mOperation.mCancelWatchdog.run();
+
+ assertThat(mOperation.isFinished()).isTrue();
+ assertThat(mOperation.isCanceling()).isFalse();
+ verify(mClientMonitor).destroy();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index d192697827f6..ac0831983262 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,10 +38,13 @@ import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,16 +53,18 @@ import androidx.test.filters.SmallTest;
import com.android.server.biometrics.nano.BiometricSchedulerProto;
import com.android.server.biometrics.nano.BiometricsProto;
-import com.android.server.biometrics.sensors.BiometricScheduler.Operation;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class BiometricSchedulerTest {
private static final String TAG = "BiometricSchedulerTest";
@@ -76,8 +85,9 @@ public class BiometricSchedulerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mToken = new Binder();
- mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
- null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
+ mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()),
+ BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */,
+ mBiometricService, LOG_NUM_RECENT_OPERATIONS,
CoexCoordinator.getInstance());
}
@@ -86,9 +96,9 @@ public class BiometricSchedulerTest {
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
final HalClientMonitor<Object> client1 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
final HalClientMonitor<Object> client2 =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client1);
mScheduler.scheduleClientMonitor(client2);
@@ -99,20 +109,17 @@ public class BiometricSchedulerTest {
@Test
public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() {
// Even if second client has a non-null daemon, it needs to be canceled.
- Object daemon2 = mock(Object.class);
-
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(
+ mContext, mToken, () -> null);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(
+ mContext, mToken, () -> mock(Object.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -122,11 +129,11 @@ public class BiometricSchedulerTest {
mScheduler.scheduleClientMonitor(client2, callback2);
waitForIdle();
- assertTrue(client1.wasUnableToStart());
+ assertTrue(client1.mUnableToStart);
verify(callback1).onClientFinished(eq(client1), eq(false) /* success */);
verify(callback1, never()).onClientStarted(any());
- assertTrue(client2.wasUnableToStart());
+ assertTrue(client2.mUnableToStart);
verify(callback2).onClientFinished(eq(client2), eq(false) /* success */);
verify(callback2, never()).onClientStarted(any());
@@ -138,21 +145,19 @@ public class BiometricSchedulerTest {
// Second non-BiometricPrompt client has a valid daemon
final Object daemon2 = mock(Object.class);
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null;
- final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2;
-
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client1 =
- new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
- final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
+ new TestAuthenticationClient(mContext, () -> null, mToken, listener1);
+ final TestHalClientMonitor client2 =
+ new TestHalClientMonitor(mContext, mToken, () -> daemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
- mScheduler.mCurrentOperation = new BiometricScheduler.Operation(
+ mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -172,8 +177,8 @@ public class BiometricSchedulerTest {
verify(callback1, never()).onClientStarted(any());
// Client 2 was able to start
- assertFalse(client2.wasUnableToStart());
- assertTrue(client2.hasStarted());
+ assertFalse(client2.mUnableToStart);
+ assertTrue(client2.mStarted);
verify(callback2).onClientStarted(eq(client2));
}
@@ -187,16 +192,18 @@ public class BiometricSchedulerTest {
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState);
- assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
+ assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor());
assertEquals(0, mScheduler.mPendingOperations.size());
// Request it to be canceled. The operation can be canceled immediately, and the scheduler
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ waitForIdle();
assertTrue(client1.isAlreadyDone());
assertTrue(client1.mDestroyed);
+ assertFalse(client1.mStartedHal);
assertNull(mScheduler.mCurrentOperation);
}
@@ -210,8 +217,8 @@ public class BiometricSchedulerTest {
// assertEquals(0, bsp.recentOperations.length);
// Pretend the scheduler is busy enrolling, and check the proto dump again.
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
bsp = getDump(true /* clearSchedulerBuffer */);
@@ -230,8 +237,8 @@ public class BiometricSchedulerTest {
@Test
public void testProtoDump_fifo() throws Exception {
// Add the first operation
- final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_ENROLL);
+ final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL);
mScheduler.scheduleClientMonitor(client);
waitForIdle();
BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */);
@@ -244,8 +251,8 @@ public class BiometricSchedulerTest {
client.getCallback().onClientFinished(client, true);
// Add another operation
- final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_REMOVE);
+ final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE);
mScheduler.scheduleClientMonitor(client2);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -256,8 +263,8 @@ public class BiometricSchedulerTest {
client2.getCallback().onClientFinished(client2, true);
// And another operation
- final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken,
- () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE);
+ final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken,
+ () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE);
mScheduler.scheduleClientMonitor(client3);
waitForIdle();
bsp = getDump(false /* clearSchedulerBuffer */);
@@ -290,8 +297,7 @@ public class BiometricSchedulerTest {
@Test
public void testCancelPendingAuth() throws RemoteException {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
-
- final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon);
+ final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon,
mToken, callback);
@@ -302,14 +308,12 @@ public class BiometricSchedulerTest {
waitForIdle();
assertEquals(mScheduler.getCurrentClient(), client1);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
// Request cancel before the authentication client has started
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
waitForIdle();
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
// Finish the blocking client. The authentication client should send ERROR_CANCELED
client1.getCallback().onClientFinished(client1, true /* success */);
@@ -326,67 +330,109 @@ public class BiometricSchedulerTest {
@Test
public void testCancels_whenAuthRequestIdNotSet() {
- testCancelsWhenRequestId(null /* requestId */, 2, true /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdNotSet_notStarted() {
- testCancelsWhenRequestId(null /* requestId */, 2, false /* started */);
+ testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches() {
- testCancelsWhenRequestId(200L, 200, true /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */);
}
@Test
public void testCancels_whenAuthRequestIdMatches_noStarted() {
- testCancelsWhenRequestId(200L, 200, false /* started */);
+ testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched() {
- testCancelsWhenRequestId(10L, 20, true /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */);
}
@Test
public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() {
- testCancelsWhenRequestId(10L, 20, false /* started */);
+ testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */);
}
- private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId,
boolean started) {
- final boolean matches = requestId == null || requestId == cancelRequestId;
final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
- final TestAuthenticationClient client = new TestAuthenticationClient(
- mContext, lazyDaemon, mToken, callback);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback));
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdNotSet() {
+ testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */);
+ }
+
+ @Test
+ public void testCancels_whenEnrollRequestIdMatches() {
+ testCancelsEnrollWhenRequestId(200L, 200, false /* started */);
+ }
+
+ @Test
+ public void testDoesNotCancel_whenEnrollRequestIdMismatched() {
+ testCancelsEnrollWhenRequestId(10L, 20, false /* started */);
+ }
+
+ private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started) {
+ final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class);
+ final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class);
+ testCancelsWhenRequestId(requestId, cancelRequestId, started,
+ new TestEnrollClient(mContext, lazyDaemon, mToken, callback));
+ }
+
+ private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId,
+ boolean started, HalClientMonitor<?> client) {
+ final boolean matches = requestId == null || requestId == cancelRequestId;
if (requestId != null) {
client.setRequestId(requestId);
}
+ final boolean isAuth = client instanceof TestAuthenticationClient;
+ final boolean isEnroll = client instanceof TestEnrollClient;
+
mScheduler.scheduleClientMonitor(client);
if (started) {
mScheduler.startPreparedClient(client.getCookie());
}
waitForIdle();
- mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ if (isAuth) {
+ mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId);
+ } else if (isEnroll) {
+ mScheduler.cancelEnrollment(mToken, cancelRequestId);
+ } else {
+ fail("unexpected operation type");
+ }
waitForIdle();
- assertEquals(matches && started ? 1 : 0, client.mNumCancels);
+ if (isAuth) {
+ // auth clients that were waiting for cookie when canceled should never invoke the hal
+ final TestAuthenticationClient authClient = (TestAuthenticationClient) client;
+ assertEquals(matches && started ? 1 : 0, authClient.mNumCancels);
+ assertEquals(started, authClient.mStartedHal);
+ } else if (isEnroll) {
+ final TestEnrollClient enrollClient = (TestEnrollClient) client;
+ assertEquals(matches ? 1 : 0, enrollClient.mNumCancels);
+ assertTrue(enrollClient.mStartedHal);
+ }
if (matches) {
- if (started) {
- assertEquals(Operation.STATE_STARTED_CANCELING,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isCanceling());
}
} else {
- if (started) {
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
+ if (started || isEnroll) { // prep'd auth clients and enroll clients
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
} else {
- assertEquals(Operation.STATE_WAITING_FOR_COOKIE,
- mScheduler.mCurrentOperation.mState);
+ assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart());
}
}
}
@@ -411,18 +457,14 @@ public class BiometricSchedulerTest {
mScheduler.cancelAuthenticationOrDetection(mToken, 9999);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertFalse(mScheduler.mPendingOperations.getFirst().isStarted());
mScheduler.cancelAuthenticationOrDetection(mToken, requestId2);
waitForIdle();
- assertEquals(Operation.STATE_STARTED,
- mScheduler.mCurrentOperation.mState);
- assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING,
- mScheduler.mPendingOperations.getFirst().mState);
+ assertTrue(mScheduler.mCurrentOperation.isStarted());
+ assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling());
}
@Test
@@ -459,12 +501,12 @@ public class BiometricSchedulerTest {
@Test
public void testClientDestroyed_afterFinish() {
final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
- final TestClientMonitor client =
- new TestClientMonitor(mContext, mToken, nonNullDaemon);
+ final TestHalClientMonitor client =
+ new TestHalClientMonitor(mContext, mToken, nonNullDaemon);
mScheduler.scheduleClientMonitor(client);
client.mCallback.onClientFinished(client, true /* success */);
waitForIdle();
- assertTrue(client.wasDestroyed());
+ assertTrue(client.mDestroyed);
}
private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
@@ -472,8 +514,10 @@ public class BiometricSchedulerTest {
}
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
- int mNumCancels = 0;
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
boolean mDestroyed = false;
+ int mNumCancels = 0;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -488,18 +532,16 @@ public class BiometricSchedulerTest {
@Override
protected void stopHalOperation() {
-
+ mStoppedHal = true;
}
@Override
protected void startHalOperation() {
-
+ mStartedHal = true;
}
@Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
+ protected void handleLifecycleAfterAuth(boolean authenticated) {}
@Override
public boolean wasUserDetected() {
@@ -519,36 +561,59 @@ public class BiometricSchedulerTest {
}
}
- private static class TestClientMonitor2 extends TestClientMonitor {
- private final int mProtoEnum;
+ private static class TestEnrollClient extends EnrollClient<Object> {
+ boolean mStartedHal = false;
+ boolean mStoppedHal = false;
+ int mNumCancels = 0;
- public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) {
- super(context, token, lazyDaemon);
- mProtoEnum = protoEnum;
+ TestEnrollClient(@NonNull Context context,
+ @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener) {
+ super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
+ "test" /* owner */, mock(BiometricUtils.class),
+ 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID,
+ true /* shouldVibrate */);
}
@Override
- public int getProtoEnum() {
- return mProtoEnum;
+ protected void stopHalOperation() {
+ mStoppedHal = true;
+ }
+
+ @Override
+ protected void startHalOperation() {
+ mStartedHal = true;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+ mNumCancels++;
+ super.cancel();
}
}
- private static class TestClientMonitor extends HalClientMonitor<Object> {
+ private static class TestHalClientMonitor extends HalClientMonitor<Object> {
+ private final int mProtoEnum;
private boolean mUnableToStart;
private boolean mStarted;
private boolean mDestroyed;
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
@NonNull LazyDaemon<Object> lazyDaemon) {
- this(context, token, lazyDaemon, 0 /* cookie */);
+ this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER);
}
- public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, int cookie) {
+ TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token,
+ @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) {
super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */,
TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */,
0 /* statsAction */, 0 /* statsClient */);
+ mProtoEnum = protoEnum;
}
@Override
@@ -559,9 +624,7 @@ public class BiometricSchedulerTest {
@Override
public int getProtoEnum() {
- // Anything other than CM_NONE, which is used to represent "idle". Tests that need
- // real proto enums should use TestClientMonitor2
- return BiometricsProto.CM_UPDATE_ACTIVE_USER;
+ return mProtoEnum;
}
@Override
@@ -573,7 +636,7 @@ public class BiometricSchedulerTest {
@Override
protected void startHalOperation() {
-
+ mStarted = true;
}
@Override
@@ -581,22 +644,9 @@ public class BiometricSchedulerTest {
super.destroy();
mDestroyed = true;
}
-
- public boolean wasUnableToStart() {
- return mUnableToStart;
- }
-
- public boolean hasStarted() {
- return mStarted;
- }
-
- public boolean wasDestroyed() {
- return mDestroyed;
- }
-
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 7fccd49db04b..407f5fb04adf 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors;
+import static android.testing.TestableLooper.RunWithLooper;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -28,52 +30,53 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.biometrics.IBiometricService;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class UserAwareBiometricSchedulerTest {
- private static final String TAG = "BiometricSchedulerTest";
+ private static final String TAG = "UserAwareBiometricSchedulerTest";
private static final int TEST_SENSOR_ID = 0;
+ private Handler mHandler;
private UserAwareBiometricScheduler mScheduler;
- private IBinder mToken;
+ private IBinder mToken = new Binder();
@Mock
private Context mContext;
@Mock
private IBiometricService mBiometricService;
- private TestUserStartedCallback mUserStartedCallback;
- private TestUserStoppedCallback mUserStoppedCallback;
+ private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback();
+ private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback();
private int mCurrentUserId = UserHandle.USER_NULL;
- private boolean mStartOperationsFinish;
- private int mStartUserClientCount;
+ private boolean mStartOperationsFinish = true;
+ private int mStartUserClientCount = 0;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- mToken = new Binder();
- mStartOperationsFinish = true;
- mStartUserClientCount = 0;
- mUserStartedCallback = new TestUserStartedCallback();
- mUserStoppedCallback = new TestUserStoppedCallback();
-
+ mHandler = new Handler(TestableLooper.get(this).getLooper());
mScheduler = new UserAwareBiometricScheduler(TAG,
+ mHandler,
BiometricScheduler.SENSOR_TYPE_UNKNOWN,
null /* gestureAvailabilityDispatcher */,
mBiometricService,
@@ -117,7 +120,7 @@ public class UserAwareBiometricSchedulerTest {
mCurrentUserId = UserHandle.USER_NULL;
mStartOperationsFinish = false;
- final BaseClientMonitor[] nextClients = new BaseClientMonitor[] {
+ final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class),
mock(BaseClientMonitor.class)
@@ -147,11 +150,11 @@ public class UserAwareBiometricSchedulerTest {
waitForIdle();
final TestStartUserClient startUserClient =
- (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor;
+ (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor();
mScheduler.reset();
assertNull(mScheduler.mCurrentOperation);
- final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation(
+ final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
mScheduler.mCurrentOperation = fakeOperation;
startUserClient.mCallback.onClientFinished(startUserClient, true);
@@ -194,8 +197,8 @@ public class UserAwareBiometricSchedulerTest {
verify(nextClient).start(any());
}
- private static void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ private void waitForIdle() {
+ TestableLooper.get(this).processAllMessages();
}
private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index a13dff21439d..0891eca9f61c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -79,6 +79,7 @@ public class SensorTest {
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */,
() -> USER_ID,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 39c51d5f5e5e..21a7a8ae65b9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -32,7 +32,9 @@ import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -69,6 +71,7 @@ public class Face10Test {
@Mock
private BiometricScheduler mScheduler;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private LockoutResetDispatcher mLockoutResetDispatcher;
private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10;
private IBinder mBinder;
@@ -97,7 +100,7 @@ public class Face10Test {
resetLockoutRequiresChallenge);
Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
- mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
+ mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler);
mBinder = new Binder();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 0d520ca9a4e4..a012b8b06c7f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -79,6 +79,7 @@ public class SensorTest {
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG,
+ new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FP_OTHER,
null /* gestureAvailabilityDispatcher */,
() -> USER_ID,
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 419dda57c568..5fcb8029af31 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,7 +22,10 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
@@ -179,6 +182,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -466,6 +470,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
+ verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
+ verify(mHistoryManager).onBootPhaseAppsCanStart();
mService.setAudioManager(mAudioManager);
@@ -1437,6 +1444,62 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+ "true",
+ false);
+ Thread.sleep(300);
+
+ final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
+
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, tag, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(notifs[0].getNotification().flags).isEqualTo(
+ FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR);
+ }
+
+ @Test
+ public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
+ "false",
+ false);
+ Thread.sleep(300);
+
+ final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
+
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ waitForIdle();
+
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(PKG);
+ assertThat(notifs[0].getNotification().flags).isEqualTo(
+ FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
+ }
+
+ @Test
public void testCancelNonexistentNotification() throws Exception {
mBinderService.cancelNotificationWithTag(PKG, PKG,
"testCancelNonexistentNotification", 0, 0);
@@ -1757,21 +1820,152 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllCancelNotificationsFromListener_NoClearFlag() throws Exception {
+ public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
+ Notification n =
+ new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(sbn.getPackageName());
+ assertEquals(0, notifs[0].getNotification().flags & FLAG_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+ sbn.getNotification().flags =
+ Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+ assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+ assertEquals(0, mService.getNotificationRecordCount());
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
+ throws Exception {
+ mService.isSystemUid = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(3, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationWithTag(
+ parent.getSbn().getPackageName(), parent.getSbn().getPackageName(),
+ parent.getSbn().getTag(), parent.getSbn().getId(), parent.getSbn().getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- mService.getBinderService().cancelNotificationsFromListener(null, null);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -1779,22 +1973,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
+ public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
+ throws Exception {
+ mService.isSystemUid = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- parent.getUserId());
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -1802,43 +1998,126 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
- Notification n =
- new Notification.Builder(mContext, mTestNotificationChannel.getId())
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
- StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, null, mUid, 0,
- n, UserHandle.getUserHandleForUid(mUid), null, 0);
- sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
- sbn.getUserId());
+ public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearChild()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
waitForIdle();
StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(sbn.getPackageName());
- assertEquals(0, notifs[0].getNotification().flags & FLAG_FOREGROUND_SERVICE);
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
}
@Test
- public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
- sbn.getNotification().flags =
- Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
- mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
- sbn.getUserId());
+ public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearParent()
+ throws Exception {
+ mService.isSystemUid = false;
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelAllNotifications(
+ parent.getSbn().getPackageName(), parent.getSbn().getUserId());
waitForIdle();
- assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
- assertEquals(0, mService.getNotificationRecordCount());
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithOngoingChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
}
@Test
- public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlag()
+ public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
@@ -1861,15 +2140,104 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlagWithParameter()
+ public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearParent()
throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_NO_CLEAR;
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_GroupWithNoClearChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_Ongoing()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_NoClear()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_clearAll_Fgs()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(child2);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithOngoingParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -1886,7 +2254,58 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+ public void testCancelNotificationsFromListener_byKey_GroupWithOngoingChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
+ throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -1900,8 +2319,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- parent.getUserId());
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithNoClearParent()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ parent.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -1909,6 +2354,76 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testCancelNotificationsFromListener_byKey_GroupWithNoClearChild()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+ child2.getSbn().getKey(), newGroup.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_Ongoing()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_ONGOING_EVENT;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_NoClear()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_NO_CLEAR;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_byKey_Fgs()
+ throws Exception {
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(child2);
+ String[] keys = {child2.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(child2.getSbn().getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
public void testGroupInstanceIds() throws Exception {
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
@@ -1973,7 +2488,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllNotifications_CancelsNoClearFlagOnGoing() throws Exception {
+ public void testCancelAllNotificationsInt_CancelsNoClearFlagOnGoing() throws Exception {
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
@@ -1987,32 +2502,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllCancelNotificationsFromListener_NoClearFlagWithParameter()
- throws Exception {
- final NotificationRecord parent = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- final NotificationRecord child = generateNotificationRecord(
- mTestNotificationChannel, 2, "group", false);
- final NotificationRecord child2 = generateNotificationRecord(
- mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- final NotificationRecord newGroup = generateNotificationRecord(
- mTestNotificationChannel, 4, "group2", false);
- mService.addNotification(parent);
- mService.addNotification(child);
- mService.addNotification(child2);
- mService.addNotification(newGroup);
- String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
- child2.getSbn().getKey(), newGroup.getSbn().getKey()};
- mService.getBinderService().cancelNotificationsFromListener(null, keys);
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(0, notifs.length);
- }
-
- @Test
- public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ public void testAppInitiatedCancelAllNotifications_CancelsOngoingFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2026,7 +2516,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ public void testCancelAllNotificationsInt_CancelsOngoingFlag() throws Exception {
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
@@ -2040,22 +2530,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testUserInitiatedCancelAllOnClearAll_OnGoingFlag() throws Exception {
- final NotificationRecord notif = generateNotificationRecord(
- mTestNotificationChannel, 1, "group", true);
- notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mService.addNotification(notif);
-
- mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
- notif.getUserId());
- waitForIdle();
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
- assertEquals(1, notifs.length);
- }
-
- @Test
- public void testCancelAllCancelNotificationsFromListener_OnGoingFlag() throws Exception {
+ public void testUserInitiatedCancelAllWithGroup_OngoingFlag() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2069,7 +2544,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- mService.getBinderService().cancelNotificationsFromListener(null, null);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
@@ -2077,39 +2553,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testCancelAllCancelNotificationsFromListener_OnGoingFlagWithParameter()
- throws Exception {
+ public void testUserInitiatedCancelAllWithGroup_NoClearFlag() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
mService.addNotification(newGroup);
- String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
- child2.getSbn().getKey(), newGroup.getSbn().getKey()};
- mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(0, notifs.length);
+ assertEquals(1, notifs.length);
}
@Test
- public void testUserInitiatedCancelAllWithGroup_OnGoingFlag() throws Exception {
+ public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
mService.addNotification(parent);
@@ -2121,7 +2595,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
- assertEquals(1, notifs.length);
+ assertEquals(0, notifs.length);
}
@Test
@@ -3396,7 +3870,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mTestNotificationChannel.getId())
.setContentTitle("foo")
.setColorized(true).setColor(Color.WHITE)
- .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+ .setFlag(FLAG_CAN_COLORIZE, true)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"testNoFakeColorizedPermission", mUid, 0,
@@ -7469,17 +7943,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testOnBootPhase() {
- mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
-
- verify(mHistoryManager, never()).onBootPhaseAppsCanStart();
-
- mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
-
- verify(mHistoryManager, times(1)).onBootPhaseAppsCanStart();
- }
-
- @Test
public void testHandleOnPackageChanged() {
String[] pkgs = new String[] {PKG, PKG_N_MR1};
int[] uids = new int[] {mUid, UserHandle.PER_USER_RANGE + 1};
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 07d467bc07d5..ea5bf52af905 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -291,7 +291,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(ITYPE_NAVIGATION_BAR).isVisible());
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -319,7 +320,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -351,7 +353,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
waitUntilWindowAnimatorIdle();
InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
@@ -402,7 +405,8 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
- policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR});
+ policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR},
+ true /* isGestureOnSystemBar */);
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
policy.updateBarControlTarget(app2);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index f8c84df53749..94bc7f2dc0d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -332,7 +332,8 @@ public class InsetsStateControllerTest extends WindowTestsBase {
assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
provider.getSource().setVisible(false);
- mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR });
+ mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR },
+ true /* isGestureOnSystemBar */);
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR));
assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 0c65cc40bd82..286cff90daab 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -688,6 +688,8 @@ class UsbUserPermissionManager {
String packageName,
PendingIntent pi,
int uid) {
+ boolean throwException = false;
+
// compare uid with packageName to foil apps pretending to be someone else
try {
ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
@@ -695,11 +697,13 @@ class UsbUserPermissionManager {
Slog.w(TAG, "package " + packageName
+ " does not match caller's uid " + uid);
EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, "");
- throw new IllegalArgumentException("package " + packageName
- + " not found");
+ throwException = true;
}
} catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("package " + packageName + " not found");
+ throwException = true;
+ } finally {
+ if (throwException)
+ throw new IllegalArgumentException("package " + packageName + " not found");
}
requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7861b11158cd..37b4e657973b 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -632,8 +632,9 @@ public abstract class CallScreeningService extends Service {
* post-dial digits are passed.
* <p>
* Calls with a {@link Call.Details#getHandlePresentation()} of
- * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN}
- * or {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
+ * {@link TelecomManager#PRESENTATION_RESTRICTED}, {@link TelecomManager#PRESENTATION_UNKNOWN},
+ * {@link TelecomManager#PRESENTATION_UNAVAILABLE} or
+ * {@link TelecomManager#PRESENTATION_PAYPHONE} presentation are not provided to the
* {@link CallScreeningService}.
*
* @param callDetails Information about a new call, see {@link Call.Details}.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 0dc899e392a7..6279bf88ab1c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -990,6 +990,11 @@ public class TelecomManager {
*/
public static final int PRESENTATION_PAYPHONE = 4;
+ /**
+ * Indicates that the address or number of a call is unavailable.
+ */
+ public static final int PRESENTATION_UNAVAILABLE = 5;
+
/*
* Values for the adb property "persist.radio.videocall.audio.output"
@@ -1006,7 +1011,7 @@ public class TelecomManager {
@IntDef(
prefix = { "PRESENTATION_" },
value = {PRESENTATION_ALLOWED, PRESENTATION_RESTRICTED, PRESENTATION_UNKNOWN,
- PRESENTATION_PAYPHONE})
+ PRESENTATION_PAYPHONE, PRESENTATION_UNAVAILABLE})
public @interface Presentation {}
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index fa70c33965ed..d77bf672347a 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -81,6 +81,13 @@ public final class CallQuality implements Parcelable {
private boolean mRtpInactivityDetected;
private boolean mRxSilenceDetected;
private boolean mTxSilenceDetected;
+ private int mNumVoiceFrames;
+ private int mNumNoDataFrames;
+ private int mNumDroppedRtpPackets;
+ private long mMinPlayoutDelayMillis;
+ private long mMaxPlayoutDelayMillis;
+ private int mNumRtpSidPacketsRx;
+ private int mNumRtpDuplicatePackets;
/** @hide **/
public CallQuality(Parcel in) {
@@ -98,6 +105,13 @@ public final class CallQuality implements Parcelable {
mRtpInactivityDetected = in.readBoolean();
mRxSilenceDetected = in.readBoolean();
mTxSilenceDetected = in.readBoolean();
+ mNumVoiceFrames = in.readInt();
+ mNumNoDataFrames = in.readInt();
+ mNumDroppedRtpPackets = in.readInt();
+ mMinPlayoutDelayMillis = in.readLong();
+ mMaxPlayoutDelayMillis = in.readLong();
+ mNumRtpSidPacketsRx = in.readInt();
+ mNumRtpDuplicatePackets = in.readInt();
}
/** @hide **/
@@ -298,6 +312,59 @@ public final class CallQuality implements Parcelable {
}
/**
+ * Returns the number of Voice frames sent by jitter buffer to audio
+ */
+ public int getNumVoiceFrames() {
+ return mNumVoiceFrames;
+ }
+
+ /**
+ * Returns the number of no-data frames sent by jitter buffer to audio
+ */
+ public int getNumNoDataFrames() {
+ return mNumNoDataFrames;
+ }
+
+ /**
+ * Returns the number of RTP voice packets dropped by jitter buffer
+ */
+ public int getNumDroppedRtpPackets() {
+ return mNumDroppedRtpPackets;
+ }
+
+ /**
+ * Returns the minimum playout delay in the reporting interval
+ * in milliseconds.
+ */
+ public long getMinPlayoutDelayMillis() {
+ return mMinPlayoutDelayMillis;
+ }
+
+ /**
+ * Returns the maximum playout delay in the reporting interval
+ * in milliseconds.
+ */
+ public long getMaxPlayoutDelayMillis() {
+ return mMaxPlayoutDelayMillis;
+ }
+
+ /**
+ * Returns the total number of RTP SID (Silence Insertion Descriptor) packets
+ * received by this device for an ongoing call
+ */
+ public int getNumRtpSidPacketsRx() {
+ return mNumRtpSidPacketsRx;
+ }
+
+ /**
+ * Returns the total number of RTP duplicate packets received by this device
+ * for an ongoing call
+ */
+ public int getNumRtpDuplicatePackets() {
+ return mNumRtpDuplicatePackets;
+ }
+
+ /**
* Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
* {@link ImsStreamMediaProfile}.
*
@@ -345,6 +412,13 @@ public final class CallQuality implements Parcelable {
+ " rtpInactivityDetected=" + mRtpInactivityDetected
+ " txSilenceDetected=" + mTxSilenceDetected
+ " rxSilenceDetected=" + mRxSilenceDetected
+ + " numVoiceFrames=" + mNumVoiceFrames
+ + " numNoDataFrames=" + mNumNoDataFrames
+ + " numDroppedRtpPackets=" + mNumDroppedRtpPackets
+ + " minPlayoutDelayMillis=" + mMinPlayoutDelayMillis
+ + " maxPlayoutDelayMillis=" + mMaxPlayoutDelayMillis
+ + " numRtpSidPacketsRx=" + mNumRtpSidPacketsRx
+ + " numRtpDuplicatePackets=" + mNumRtpDuplicatePackets
+ "}";
}
@@ -364,7 +438,14 @@ public final class CallQuality implements Parcelable {
mCodecType,
mRtpInactivityDetected,
mRxSilenceDetected,
- mTxSilenceDetected);
+ mTxSilenceDetected,
+ mNumVoiceFrames,
+ mNumNoDataFrames,
+ mNumDroppedRtpPackets,
+ mMinPlayoutDelayMillis,
+ mMaxPlayoutDelayMillis,
+ mNumRtpSidPacketsRx,
+ mNumRtpDuplicatePackets);
}
@Override
@@ -392,7 +473,14 @@ public final class CallQuality implements Parcelable {
&& mCodecType == s.mCodecType
&& mRtpInactivityDetected == s.mRtpInactivityDetected
&& mRxSilenceDetected == s.mRxSilenceDetected
- && mTxSilenceDetected == s.mTxSilenceDetected);
+ && mTxSilenceDetected == s.mTxSilenceDetected
+ && mNumVoiceFrames == s.mNumVoiceFrames
+ && mNumNoDataFrames == s.mNumNoDataFrames
+ && mNumDroppedRtpPackets == s.mNumDroppedRtpPackets
+ && mMinPlayoutDelayMillis == s.mMinPlayoutDelayMillis
+ && mMaxPlayoutDelayMillis == s.mMaxPlayoutDelayMillis
+ && mNumRtpSidPacketsRx == s.mNumRtpSidPacketsRx
+ && mNumRtpDuplicatePackets == s.mNumRtpDuplicatePackets);
}
/**
@@ -420,6 +508,13 @@ public final class CallQuality implements Parcelable {
dest.writeBoolean(mRtpInactivityDetected);
dest.writeBoolean(mRxSilenceDetected);
dest.writeBoolean(mTxSilenceDetected);
+ dest.writeInt(mNumVoiceFrames);
+ dest.writeInt(mNumNoDataFrames);
+ dest.writeInt(mNumDroppedRtpPackets);
+ dest.writeLong(mMinPlayoutDelayMillis);
+ dest.writeLong(mMaxPlayoutDelayMillis);
+ dest.writeInt(mNumRtpSidPacketsRx);
+ dest.writeInt(mNumRtpDuplicatePackets);
}
public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
@@ -431,4 +526,322 @@ public final class CallQuality implements Parcelable {
return new CallQuality[size];
}
};
+
+ /**
+ * Provides a convenient way to set the fields of a {@link CallQuality} when creating a new
+ * instance.
+ *
+ * <p>The example below shows how you might create a new {@code CallQuality}:
+ *
+ * <pre><code>
+ *
+ * CallQuality callQuality = new CallQuality.Builder()
+ * .setNumRtpPacketsTransmitted(150)
+ * .setNumRtpPacketsReceived(200)
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+
+ private int mDownlinkCallQualityLevel;
+ private int mUplinkCallQualityLevel;
+ private int mCallDuration;
+ private int mNumRtpPacketsTransmitted;
+ private int mNumRtpPacketsReceived;
+ private int mNumRtpPacketsTransmittedLost;
+ private int mNumRtpPacketsNotReceived;
+ private int mAverageRelativeJitter;
+ private int mMaxRelativeJitter;
+ private int mAverageRoundTripTime;
+ private int mCodecType;
+ private boolean mRtpInactivityDetected;
+ private boolean mRxSilenceDetected;
+ private boolean mTxSilenceDetected;
+ private int mNumVoiceFrames;
+ private int mNumNoDataFrames;
+ private int mNumDroppedRtpPackets;
+ private long mMinPlayoutDelayMillis;
+ private long mMaxPlayoutDelayMillis;
+ private int mNumRtpSidPacketsRx;
+ private int mNumRtpDuplicatePackets;
+
+ /**
+ * Set the downlink call quality level for ongoing call.
+ *
+ * @param downlinkCallQualityLevel the Downlink call quality level
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setDownlinkCallQualityLevel(
+ @CallQualityLevel int downlinkCallQualityLevel) {
+ mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+ return this;
+ }
+
+ /**
+ * Set the uplink call quality level for ongoing call.
+ *
+ * @param uplinkCallQualityLevel the Uplink call quality level
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setUplinkCallQualityLevel(
+ @CallQualityLevel int uplinkCallQualityLevel) {
+ mUplinkCallQualityLevel = uplinkCallQualityLevel;
+ return this;
+ }
+
+ /**
+ * Set the call duration in milliseconds.
+ *
+ * @param callDuration the call duration in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCallDuration(int callDuration) {
+ mCallDuration = callDuration;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets sent for ongoing call.
+ *
+ * @param numRtpPacketsTransmitted RTP packets sent to network
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsTransmitted(int numRtpPacketsTransmitted) {
+ mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets received for ongoing call.
+ *
+ * @param numRtpPacketsReceived RTP packets received from network
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsReceived(int numRtpPacketsReceived) {
+ mNumRtpPacketsReceived = numRtpPacketsReceived;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets which were lost in network and never
+ * transmitted.
+ *
+ * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+ * transmitted
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsTransmittedLost(int numRtpPacketsTransmittedLost) {
+ mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP packets which were lost in network and never received.
+ *
+ * @param numRtpPacketsNotReceived RTP packets which were lost in network and
+ * never received
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpPacketsNotReceived(int numRtpPacketsNotReceived) {
+ mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+ return this;
+ }
+
+ /**
+ * Set the average relative jitter in milliseconds.
+ *
+ * @param averageRelativeJitter average relative jitter in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setAverageRelativeJitter(int averageRelativeJitter) {
+ mAverageRelativeJitter = averageRelativeJitter;
+ return this;
+ }
+
+ /**
+ * Set the maximum relative jitter in milliseconds.
+ *
+ * @param maxRelativeJitter maximum relative jitter in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMaxRelativeJitter(int maxRelativeJitter) {
+ mMaxRelativeJitter = maxRelativeJitter;
+ return this;
+ }
+
+ /**
+ * Set the average round trip delay in milliseconds.
+ *
+ * @param averageRoundTripTime average round trip delay in milliseconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setAverageRoundTripTime(int averageRoundTripTime) {
+ mAverageRoundTripTime = averageRoundTripTime;
+ return this;
+ }
+
+ /**
+ * Set the codec type used in the ongoing call.
+ *
+ * @param codecType the codec type.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setCodecType(int codecType) {
+ mCodecType = codecType;
+ return this;
+ }
+
+ /**
+ * Set to be True if no incoming RTP is received for a continuous
+ * duration of 4 seconds.
+ *
+ * @param rtpInactivityDetected True if no incoming RTP is received for
+ * a continuous duration of 4 seconds
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setRtpInactivityDetected(boolean rtpInactivityDetected) {
+ mRtpInactivityDetected = rtpInactivityDetected;
+ return this;
+ }
+
+ /**
+ * Set to be True if only silence RTP packets are received for 20 seconds
+ * immediately after call is connected.
+ *
+ * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+ * immediately after call is connected
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setIncomingSilenceDetectedAtCallSetup(boolean rxSilenceDetected) {
+ mRxSilenceDetected = rxSilenceDetected;
+ return this;
+ }
+
+ /**
+ * Set to be True if only silence RTP packets are sent for 20 seconds immediately
+ * after call is connected.
+ *
+ * @param txSilenceDetected True if only silence RTP packets are sent for
+ * 20 seconds immediately after call is connected.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setOutgoingSilenceDetectedAtCallSetup(boolean txSilenceDetected) {
+ mTxSilenceDetected = txSilenceDetected;
+ return this;
+ }
+
+ /**
+ * Set the number of voice frames sent by jitter buffer to audio.
+ *
+ * @param numVoiceFrames Voice frames sent by jitter buffer to audio.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumVoiceFrames(int numVoiceFrames) {
+ mNumVoiceFrames = numVoiceFrames;
+ return this;
+ }
+
+ /**
+ * Set the number of no-data frames sent by jitter buffer to audio.
+ *
+ * @param numNoDataFrames no-data frames sent by jitter buffer to audio
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumNoDataFrames(int numNoDataFrames) {
+ mNumNoDataFrames = numNoDataFrames;
+ return this;
+ }
+
+ /**
+ * Set the number of RTP Voice packets dropped by jitter buffer.
+ *
+ * @param numDroppedRtpPackets number of RTP Voice packets dropped by jitter buffer
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumDroppedRtpPackets(int numDroppedRtpPackets) {
+ mNumDroppedRtpPackets = numDroppedRtpPackets;
+ return this;
+ }
+
+ /**
+ * Set the minimum playout delay in the reporting interval in milliseconds.
+ *
+ * @param minPlayoutDelayMillis minimum playout delay in the reporting interval
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMinPlayoutDelayMillis(long minPlayoutDelayMillis) {
+ mMinPlayoutDelayMillis = minPlayoutDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set the maximum Playout delay in the reporting interval in milliseconds.
+ *
+ * @param maxPlayoutDelayMillis maximum Playout delay in the reporting interval
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setMaxPlayoutDelayMillis(long maxPlayoutDelayMillis) {
+ mMaxPlayoutDelayMillis = maxPlayoutDelayMillis;
+ return this;
+ }
+
+ /**
+ * Set the total number of RTP SID (Silence Insertion Descriptor)
+ * packets received by this device for an ongoing call.
+ *
+ * @param numRtpSidPacketsRx the total number of RTP SID packets received
+ * by this device for an ongoing call.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpSidPacketsRx(int numRtpSidPacketsRx) {
+ mNumRtpSidPacketsRx = numRtpSidPacketsRx;
+ return this;
+ }
+
+ /**
+ * Set the total number of RTP duplicate packets received by this device
+ * for an ongoing call.
+ *
+ * @param numRtpDuplicatePackets the total number of RTP duplicate packets
+ * received by this device for an ongoing call
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setNumRtpDuplicatePackets(int numRtpDuplicatePackets) {
+ mNumRtpDuplicatePackets = numRtpDuplicatePackets;
+ return this;
+ }
+
+ /**
+ * Build the CallQuality.
+ *
+ * @return the CallQuality object.
+ */
+ public @NonNull CallQuality build() {
+
+ CallQuality callQuality = new CallQuality();
+ callQuality.mDownlinkCallQualityLevel = mDownlinkCallQualityLevel;
+ callQuality.mUplinkCallQualityLevel = mUplinkCallQualityLevel;
+ callQuality.mCallDuration = mCallDuration;
+ callQuality.mNumRtpPacketsTransmitted = mNumRtpPacketsTransmitted;
+ callQuality.mNumRtpPacketsReceived = mNumRtpPacketsReceived;
+ callQuality.mNumRtpPacketsTransmittedLost = mNumRtpPacketsTransmittedLost;
+ callQuality.mNumRtpPacketsNotReceived = mNumRtpPacketsNotReceived;
+ callQuality.mAverageRelativeJitter = mAverageRelativeJitter;
+ callQuality.mMaxRelativeJitter = mMaxRelativeJitter;
+ callQuality.mAverageRoundTripTime = mAverageRoundTripTime;
+ callQuality.mCodecType = mCodecType;
+ callQuality.mRtpInactivityDetected = mRtpInactivityDetected;
+ callQuality.mTxSilenceDetected = mTxSilenceDetected;
+ callQuality.mRxSilenceDetected = mRxSilenceDetected;
+ callQuality.mNumVoiceFrames = mNumVoiceFrames;
+ callQuality.mNumNoDataFrames = mNumNoDataFrames;
+ callQuality.mNumDroppedRtpPackets = mNumDroppedRtpPackets;
+ callQuality.mMinPlayoutDelayMillis = mMinPlayoutDelayMillis;
+ callQuality.mMaxPlayoutDelayMillis = mMaxPlayoutDelayMillis;
+ callQuality.mNumRtpSidPacketsRx = mNumRtpSidPacketsRx;
+ callQuality.mNumRtpDuplicatePackets = mNumRtpDuplicatePackets;
+
+ return callQuality;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cd399c02a72e..d604dc1d2c92 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2273,6 +2273,7 @@ public class CarrierConfigManager {
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
* android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE
*
* <p>
* 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
@@ -5035,6 +5036,10 @@ public class CarrierConfigManager {
/** Specifies the PCO id for IPv4 Epdg server address */
public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
+ /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
+ public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL =
+ KEY_PREFIX + "enable_support_for_eap_aka_fast_reauth_bool";
+
/** @hide */
@IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
public @interface AuthenticationMethodType {}
@@ -5178,6 +5183,7 @@ public class CarrierConfigManager {
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
+ defaults.putBoolean(KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL, false);
return defaults;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2295ed7f3876..574a356c43f2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -194,7 +194,7 @@ public class SubscriptionManager {
}
@Override
- protected T recompute(Void aVoid) {
+ public T recompute(Void aVoid) {
T result = mDefaultValue;
try {
@@ -228,7 +228,7 @@ public class SubscriptionManager {
}
@Override
- protected T recompute(Integer query) {
+ public T recompute(Integer query) {
T result = mDefaultValue;
try {
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 93e10583fbc0..7e2d80eb871c 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -317,6 +317,10 @@ public final class ImsCallProfile implements Parcelable {
* Payphone presentation for Originating Identity.
*/
public static final int OIR_PRESENTATION_PAYPHONE = 4;
+ /**
+ * Unavailable presentation for Originating Identity.
+ */
+ public static final int OIR_PRESENTATION_UNAVAILABLE = 5;
//Values for EXTRA_DIALSTRING
/**
@@ -989,6 +993,8 @@ public final class ImsCallProfile implements Parcelable {
return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
case PhoneConstants.PRESENTATION_UNKNOWN:
return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+ case PhoneConstants.PRESENTATION_UNAVAILABLE:
+ return ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE;
default:
return ImsCallProfile.OIR_DEFAULT;
}
@@ -1017,6 +1023,8 @@ public final class ImsCallProfile implements Parcelable {
return PhoneConstants.PRESENTATION_ALLOWED;
case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
return PhoneConstants.PRESENTATION_PAYPHONE;
+ case ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE:
+ return PhoneConstants.PRESENTATION_UNAVAILABLE;
case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
return PhoneConstants.PRESENTATION_UNKNOWN;
default:
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index f6502466e25e..813e80e6f355 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -95,6 +95,7 @@ public class PhoneConstants {
public static final int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network
@UnsupportedAppUsage
public static final int PRESENTATION_PAYPHONE = 4; // show pay phone info
+ public static final int PRESENTATION_UNAVAILABLE = 5; // show unavailable
public static final String PHONE_NAME_KEY = "phoneName";
public static final String DATA_NETWORK_TYPE_KEY = "networkType";
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index eef7b57ff4b2..e07a8f94d651 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -99,11 +99,7 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appLayerReplacesLauncher()
- }
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dfa8f8ea1ac9..cd209b2cfb5b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -96,20 +96,14 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appWindowReplacesLauncherAsTopWindow() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appWindowReplacesLauncherAsTopWindow()
- }
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@FlakyTest
@@ -119,11 +113,7 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() {
- // This test doesn't work in shell transitions because of b/206094140
- assumeFalse(isShellTransitionsEnabled)
- super.appLayerReplacesLauncher()
- }
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index cb37fc7b47e9..0a88f6bb5908 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:supportsRtl="true">
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
<intent-filter>
@@ -32,6 +33,7 @@
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:exported="true">
<intent-filter>
@@ -40,6 +42,7 @@
</intent-filter>
</activity>
<activity android:name=".ImeActivityAutoFocus"
+ android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
android:label="ImeAppAutoFocus"
@@ -51,6 +54,7 @@
</activity>
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="SeamlessApp"
android:exported="true">
@@ -60,6 +64,7 @@
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
+ android:theme="@style/CutoutShortEdges"
android:resizeableActivity="false"
android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
android:label="NonResizeableApp"
@@ -72,6 +77,7 @@
</activity>
<activity android:name=".ButtonActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="ButtonActivity"
android:exported="true">
@@ -82,6 +88,7 @@
</activity>
<activity android:name=".LaunchNewTaskActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewTaskActivity"
android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 000000000000..87a61a88c094
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <style name="CutoutDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 476be44ee759..d03aee282ee1 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -15,8 +15,8 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,13 +27,13 @@ import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
-public class VcnCellUnderlyingNetworkPriorityTest {
+public class VcnCellUnderlyingNetworkTemplateTest {
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() {
- return new VcnCellUnderlyingNetworkPriority.Builder()
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
@@ -45,7 +45,7 @@ public class VcnCellUnderlyingNetworkPriorityTest {
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
@@ -56,8 +56,8 @@ public class VcnCellUnderlyingNetworkPriorityTest {
@Test
public void testBuilderAndGettersForDefaultValues() {
- final VcnCellUnderlyingNetworkPriority networkPriority =
- new VcnCellUnderlyingNetworkPriority.Builder().build();
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ new VcnCellUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
@@ -68,10 +68,10 @@ public class VcnCellUnderlyingNetworkPriorityTest {
@Test
public void testPersistableBundle() {
- final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(
networkPriority,
- VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 377f526a9825..1f2905da08f4 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -54,7 +54,7 @@ public class VcnGatewayConnectionConfigTest {
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
- private static final LinkedHashSet<VcnUnderlyingNetworkPriority> UNDERLYING_NETWORK_PRIORITIES =
+ private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES =
new LinkedHashSet();
static {
@@ -62,9 +62,9 @@ public class VcnGatewayConnectionConfigTest {
Arrays.sort(UNDERLYING_CAPS);
UNDERLYING_NETWORK_PRIORITIES.add(
- VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
UNDERLYING_NETWORK_PRIORITIES.add(
- VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -286,7 +286,7 @@ public class VcnGatewayConnectionConfigTest {
}
private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
- LinkedHashSet<VcnUnderlyingNetworkPriority> networkPriorities) {
+ LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) {
return buildTestConfigWithExposedCaps(
new VcnGatewayConnectionConfig.Builder(
"buildTestConfigWithVcnUnderlyingNetworkPriorities",
@@ -300,17 +300,17 @@ public class VcnGatewayConnectionConfigTest {
final VcnGatewayConnectionConfig config =
buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
- final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesEqual =
+ final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual =
new LinkedHashSet();
- networkPrioritiesEqual.add(VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
- networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
final VcnGatewayConnectionConfig configEqual =
buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
- final LinkedHashSet<VcnUnderlyingNetworkPriority> networkPrioritiesNotEqual =
+ final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual =
new LinkedHashSet();
networkPrioritiesNotEqual.add(
- VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
final VcnGatewayConnectionConfig configNotEqual =
buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index dd272cb38596..652057fd48c7 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -15,8 +15,8 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,13 +26,13 @@ import static org.junit.Assert.fail;
import org.junit.Test;
-public class VcnWifiUnderlyingNetworkPriorityTest {
+public class VcnWifiUnderlyingNetworkTemplateTest {
private static final String SSID = "TestWifi";
private static final int INVALID_NETWORK_QUALITY = -1;
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnWifiUnderlyingNetworkPriority getTestNetworkPriority() {
- return new VcnWifiUnderlyingNetworkPriority.Builder()
+ static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() {
+ return new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setSsid(SSID)
@@ -41,7 +41,7 @@ public class VcnWifiUnderlyingNetworkPriorityTest {
@Test
public void testBuilderAndGetters() {
- final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
assertTrue(networkPriority.allowMetered());
assertEquals(SSID, networkPriority.getSsid());
@@ -49,8 +49,8 @@ public class VcnWifiUnderlyingNetworkPriorityTest {
@Test
public void testBuilderAndGettersForDefaultValues() {
- final VcnWifiUnderlyingNetworkPriority networkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder().build();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder().build();
assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
assertFalse(networkPriority.allowMetered());
assertNull(SSID, networkPriority.getSsid());
@@ -59,7 +59,7 @@ public class VcnWifiUnderlyingNetworkPriorityTest {
@Test
public void testBuildWithInvalidNetworkQuality() {
try {
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(INVALID_NETWORK_QUALITY);
fail("Expected to fail due to the invalid network quality");
} catch (Exception expected) {
@@ -68,10 +68,10 @@ public class VcnWifiUnderlyingNetworkPriorityTest {
@Test
public void testPersistableBundle() {
- final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
assertEquals(
networkPriority,
- VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 7c7dc4d79e9a..bb98bc0bab53 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,7 +23,6 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -264,6 +263,7 @@ public class VcnManagementServiceTest {
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
+ mTestLooper.dispatchAll();
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
@@ -475,10 +475,8 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
-
- // Verify teardown after delay
- mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
+
verify(vcn).teardownAsynchronously();
verify(mMockPolicyListener).onPolicyChanged();
}
@@ -504,92 +502,6 @@ public class VcnManagementServiceTest {
assertEquals(0, mVcnMgmtSvc.getAllVcns().size());
}
- /**
- * Tests an intermediate state where carrier privileges are marked as lost before active data
- * subId changes during a SIM ejection.
- *
- * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to
- * immediately.
- */
- @Test
- public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges()
- throws Exception {
- setupActiveSubscription(TEST_UUID_2);
-
- final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
- final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
-
- // Simulate privileges lost
- triggerSubscriptionTrackerCbAndGetSnapshot(
- TEST_SUBSCRIPTION_ID,
- TEST_UUID_2,
- Collections.emptySet(),
- Collections.emptyMap(),
- false /* hasCarrierPrivileges */);
-
- // Verify teardown after delay
- mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
- mTestLooper.dispatchAll();
- verify(vcn).teardownAsynchronously();
- }
-
- @Test
- public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
- throws Exception {
- setupActiveSubscription(TEST_UUID_2);
-
- final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
- final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
-
- // Simulate SIM unloaded
- triggerSubscriptionTrackerCbAndGetSnapshot(
- INVALID_SUBSCRIPTION_ID,
- null /* activeDataSubscriptionGroup */,
- Collections.emptySet(),
- Collections.emptyMap(),
- false /* hasCarrierPrivileges */);
-
- // Simulate new SIM loaded right during teardown delay.
- mTestLooper.moveTimeForward(
- VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
- mTestLooper.dispatchAll();
- triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
-
- // Verify that even after the full timeout duration, the VCN instance is not torn down
- mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
- mTestLooper.dispatchAll();
- verify(vcn, never()).teardownAsynchronously();
- }
-
- @Test
- public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
- setupActiveSubscription(TEST_UUID_2);
-
- final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
- final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
-
- // Simulate SIM unloaded
- triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
-
- // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
- // vcnInstance.
- mTestLooper.moveTimeForward(
- VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
- mTestLooper.dispatchAll();
- mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
- triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
- final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
-
- // Verify that new instance was different, and the old one was torn down
- assertTrue(oldInstance != newInstance);
- verify(oldInstance).teardownAsynchronously();
-
- // Verify that even after the full timeout duration, the new VCN instance is not torn down
- mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
- mTestLooper.dispatchAll();
- verify(newInstance, never()).teardownAsynchronously();
- }
-
@Test
public void testPackageChangeListenerRegistered() throws Exception {
verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
@@ -925,6 +837,8 @@ public class VcnManagementServiceTest {
private void setupSubscriptionAndStartVcn(
int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
mVcnMgmtSvc.systemReady();
+ mTestLooper.dispatchAll();
+
triggerSubscriptionTrackerCbAndGetSnapshot(
subGrp,
Collections.singleton(subGrp),
@@ -1020,6 +934,7 @@ public class VcnManagementServiceTest {
private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
mVcnMgmtSvc.systemReady();
+ mTestLooper.dispatchAll();
final ArgumentCaptor<NetworkCallback> captor =
ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1264,15 +1179,14 @@ public class VcnManagementServiceTest {
true /* isActive */,
true /* hasCarrierPrivileges */);
- // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
- // timeout so the VCN goes inactive.
+ // VCN is currently active. Lose carrier privileges for TEST_PACKAGE so the VCN goes
+ // inactive.
final TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(
TEST_UUID_1,
Collections.singleton(TEST_UUID_1),
Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
false /* hasCarrierPrivileges */);
- mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
// Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 1f0df62fe72c..978bf3ed2e92 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -22,6 +22,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -34,8 +35,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -57,6 +60,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
+import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -83,7 +88,7 @@ public class TelephonySubscriptionTrackerTest {
private static final String PACKAGE_NAME =
TelephonySubscriptionTrackerTest.class.getPackage().getName();
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
- private static final int TEST_SIM_SLOT_INDEX = 1;
+ private static final int TEST_SIM_SLOT_INDEX = 0;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
private static final int TEST_SUBSCRIPTION_ID_2 = 3;
@@ -151,6 +156,8 @@ public class TelephonySubscriptionTrackerTest {
@Before
public void setUp() throws Exception {
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
mTelephonySubscriptionTracker =
new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
@@ -180,6 +187,15 @@ public class TelephonySubscriptionTrackerTest {
return captor.getValue();
}
+ private List<CarrierPrivilegesListener> getCarrierPrivilegesListeners() {
+ final ArgumentCaptor<CarrierPrivilegesListener> captor =
+ ArgumentCaptor.forClass(CarrierPrivilegesListener.class);
+ verify(mTelephonyManager, atLeastOnce())
+ .addCarrierPrivilegesListener(anyInt(), any(), captor.capture());
+
+ return captor.getAllValues();
+ }
+
private ActiveDataSubscriptionIdListener getActiveDataSubscriptionIdListener() {
final ArgumentCaptor<TelephonyCallback> captor =
ArgumentCaptor.forClass(TelephonyCallback.class);
@@ -188,6 +204,11 @@ public class TelephonySubscriptionTrackerTest {
return (ActiveDataSubscriptionIdListener) captor.getValue();
}
+ private Intent buildTestMultiSimConfigBroadcastIntent() {
+ Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
+ return intent;
+ }
+
private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
@@ -239,12 +260,21 @@ public class TelephonySubscriptionTrackerTest {
any(),
eq(mHandler));
final IntentFilter filter = getIntentFilter();
- assertEquals(1, filter.countActions());
+ assertEquals(2, filter.countActions());
assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+ assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
verify(mSubscriptionManager)
.addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
assertNotNull(getOnSubscriptionsChangedListener());
+
+ verify(mTelephonyManager, times(2))
+ .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager)
+ .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager)
+ .addCarrierPrivilegesListener(eq(1), any(HandlerExecutor.class), any());
+ assertEquals(2, getCarrierPrivilegesListeners().size());
}
@Test
@@ -255,6 +285,49 @@ public class TelephonySubscriptionTrackerTest {
final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));
+
+ for (CarrierPrivilegesListener carrierPrivilegesListener :
+ getCarrierPrivilegesListeners()) {
+ verify(mTelephonyManager)
+ .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
+ }
+ }
+
+ @Test
+ public void testMultiSimConfigChanged() throws Exception {
+ final ArrayMap<Integer, Integer> readySubIdsBySlotId = new ArrayMap<>();
+ readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1);
+ readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
+
+ mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
+
+ List<CarrierPrivilegesListener> carrierPrivilegesListeners =
+ getCarrierPrivilegesListeners();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestMultiSimConfigBroadcastIntent());
+ mTestLooper.dispatchAll();
+
+ for (CarrierPrivilegesListener carrierPrivilegesListener : carrierPrivilegesListeners) {
+ verify(mTelephonyManager)
+ .removeCarrierPrivilegesListener(eq(carrierPrivilegesListener));
+ }
+
+ // Expect cache cleared for inactive slots.
+ assertNull(
+ mTelephonySubscriptionTracker
+ .getReadySubIdsBySlotId()
+ .get(TEST_SIM_SLOT_INDEX + 1));
+
+ // Expect a new CarrierPrivilegesListener to have been registered for slot 0, and none other
+ // (2 previously registered during startup, for slots 0 & 1)
+ verify(mTelephonyManager, times(3))
+ .addCarrierPrivilegesListener(anyInt(), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager, times(2))
+ .addCarrierPrivilegesListener(eq(0), any(HandlerExecutor.class), any());
+
+ // Verify that this triggers a re-evaluation
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
}
@Test
@@ -314,6 +387,17 @@ public class TelephonySubscriptionTrackerTest {
}
@Test
+ public void testOnCarrierPrivilegesChanged() throws Exception {
+ setupReadySubIds();
+
+ final CarrierPrivilegesListener listener = getCarrierPrivilegesListeners().get(0);
+ listener.onCarrierPrivilegesChanged(Collections.emptyList(), new int[] {});
+ mTestLooper.dispatchAll();
+
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
+ }
+
+ @Test
public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 46a614f60ae7..f23d5bf67ebf 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,7 +16,7 @@
package com.android.server.vcn.routeselection;
-import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -39,10 +39,10 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
-import android.net.vcn.VcnCellUnderlyingNetworkPriority;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
-import android.net.vcn.VcnWifiUnderlyingNetworkPriority;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
@@ -142,8 +142,8 @@ public class NetworkPriorityClassifierTest {
@Test
public void testMatchWithoutNotMeteredBit() {
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(false /* allowMetered */)
.build();
@@ -161,8 +161,8 @@ public class NetworkPriorityClassifierTest {
private void verifyMatchWifi(
boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.build();
@@ -211,8 +211,8 @@ public class NetworkPriorityClassifierTest {
private void verifyMatchWifiWithSsid(boolean useMatchedSsid, boolean expectMatch) {
final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
- final VcnWifiUnderlyingNetworkPriority wifiNetworkPriority =
- new VcnWifiUnderlyingNetworkPriority.Builder()
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setSsid(nwPrioritySsid)
@@ -237,8 +237,8 @@ public class NetworkPriorityClassifierTest {
verifyMatchWifiWithSsid(false /* useMatchedSsid */, false /* expectMatch */);
}
- private static VcnCellUnderlyingNetworkPriority.Builder getCellNetworkPriorityBuilder() {
- return new VcnCellUnderlyingNetworkPriority.Builder()
+ private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
.setNetworkQuality(NETWORK_QUALITY_OK)
.setAllowMetered(true /* allowMetered */)
.setAllowRoaming(true /* allowRoaming */);
@@ -257,7 +257,7 @@ public class NetworkPriorityClassifierTest {
@Test
public void testMatchOpportunisticCell() {
- final VcnCellUnderlyingNetworkPriority opportunisticCellNetworkPriority =
+ final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
getCellNetworkPriorityBuilder()
.setRequireOpportunistic(true /* requireOpportunistic */)
.build();
@@ -277,7 +277,7 @@ public class NetworkPriorityClassifierTest {
private void verifyMatchMacroCellWithAllowedPlmnIds(
boolean useMatchedPlmnId, boolean expectMatch) {
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
.setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
@@ -306,7 +306,7 @@ public class NetworkPriorityClassifierTest {
private void verifyMatchMacroCellWithAllowedSpecificCarrierIds(
boolean useMatchedCarrierId, boolean expectMatch) {
final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
.setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
.build();
@@ -335,7 +335,7 @@ public class NetworkPriorityClassifierTest {
@Test
public void testMatchWifiFailWithoutNotRoamingBit() {
- final VcnCellUnderlyingNetworkPriority networkPriority =
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
assertFalse(