summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java407
-rw-r--r--core/api/current.txt12
-rw-r--r--core/api/system-current.txt30
-rw-r--r--core/java/android/app/ActivityManager.java17
-rw-r--r--core/java/android/app/ActivityOptions.java1
-rw-r--r--core/java/android/app/admin/SecurityLog.java18
-rw-r--r--core/java/android/app/admin/SecurityLogTags.logtags4
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl3
-rw-r--r--core/java/android/app/trust/TrustManager.java19
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java50
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java9
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java11
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java11
-rwxr-xr-xcore/java/android/os/Build.java2
-rw-r--r--core/java/android/os/Trace.java3
-rw-r--r--core/java/android/os/UserManager.java19
-rw-r--r--core/java/android/os/VibrationAttributes.java37
-rw-r--r--core/java/android/os/storage/StorageManager.java32
-rw-r--r--core/java/android/permission/PermissionManager.java15
-rw-r--r--core/java/android/service/trust/GrantTrustResult.aidl19
-rw-r--r--core/java/android/service/trust/GrantTrustResult.java179
-rw-r--r--core/java/android/service/trust/ITrustAgentService.aidl3
-rw-r--r--core/java/android/service/trust/ITrustAgentServiceCallback.aidl4
-rw-r--r--core/java/android/service/trust/TrustAgentService.java106
-rw-r--r--core/java/android/text/BoringLayout.java17
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java3
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java2
-rw-r--r--core/java/android/view/SurfaceView.java145
-rw-r--r--core/java/android/view/ViewRootImpl.java489
-rw-r--r--core/java/android/view/WindowLayout.java40
-rw-r--r--core/java/android/view/animation/Animation.java9
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java8
-rw-r--r--core/java/android/widget/Editor.java8
-rw-r--r--core/java/android/window/ClientWindowFrames.java7
-rw-r--r--core/java/android/window/SplashScreen.java8
-rw-r--r--core/java/android/window/SplashScreenView.java25
-rw-r--r--core/java/com/android/internal/app/AppLocaleStore.java85
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/app/LocaleHelper.java6
-rw-r--r--core/java/com/android/internal/app/LocalePickerWithRegion.java78
-rw-r--r--core/java/com/android/internal/app/LocaleStore.java49
-rw-r--r--core/java/com/android/internal/app/OWNERS3
-rw-r--r--core/java/com/android/internal/app/SuggestedLocaleAdapter.java40
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl2
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java4
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java44
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java1
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.java21
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/anim/activity_close_enter_legacy.xml34
-rw-r--r--core/res/res/anim/activity_close_exit_legacy.xml44
-rw-r--r--core/res/res/anim/activity_open_enter_legacy.xml42
-rw-r--r--core/res/res/anim/activity_open_exit_legacy.xml45
-rw-r--r--core/res/res/layout/app_language_picker_system_default.xml42
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/config.xml21
-rw-r--r--core/res/res/values/public-staging.xml6
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/res/res/values/symbols.xml10
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java13
-rw-r--r--data/etc/services.core.protolog.json225
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java168
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java17
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java108
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java103
-rw-r--r--media/java/android/media/tv/DsmccResponse.java63
-rw-r--r--media/jni/android_media_MediaDrm.cpp14
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java40
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java144
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java23
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java20
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java16
-rw-r--r--packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_secondary.xml22
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml2
-rw-r--r--packages/SettingsLib/res/values/strings.xml6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java56
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeAudioContentMetadata.java43
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java192
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java214
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java298
-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/SettingsState.java2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt (renamed from packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt)2
-rw-r--r--packages/SystemUI/docs/media-controls.md2
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt202
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java4
-rw-r--r--packages/SystemUI/res/drawable/new_fgs_dot.xml3
-rw-r--r--packages/SystemUI/res/drawable/qs_media_outline_button.xml (renamed from packages/SystemUI/res/drawable/qs_media_button_background.xml)0
-rw-r--r--packages/SystemUI/res/drawable/qs_media_solid_button.xml27
-rw-r--r--packages/SystemUI/res/layout/media_output_list_item.xml3
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml66
-rw-r--r--packages/SystemUI/res/layout/media_view.xml300
-rw-r--r--packages/SystemUI/res/layout/rounded_corners_bottom.xml40
-rw-r--r--packages/SystemUI/res/layout/rounded_corners_top.xml40
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/ids.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/res/xml/media_collapsed.xml176
-rw-r--r--packages/SystemUI/res/xml/media_expanded.xml174
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java269
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt192
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java328
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/PlayerSessionViewHolder.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java289
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarComponent.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java178
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt)28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java121
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java234
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java72
-rw-r--r--services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java6
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java15
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java19
-rw-r--r--services/core/java/com/android/server/SystemService.java13
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java19
-rw-r--r--services/core/java/com/android/server/am/AppRestrictionController.java6
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java77
-rw-r--r--services/core/java/com/android/server/audio/SettingsAdapter.java92
-rw-r--r--services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java25
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java9
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java107
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java47
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java3
-rw-r--r--services/core/java/com/android/server/pm/Installer.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java52
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java23
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java68
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java109
-rw-r--r--services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java18
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java17
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java3
-rw-r--r--services/core/java/com/android/server/trust/TrustAgentWrapper.java36
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java85
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java8
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java79
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java68
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java45
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java80
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java11
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java50
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java11
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java8
-rw-r--r--services/core/java/com/android/server/wm/LocalAnimationAdapter.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java1
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java9
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimationRunner.java193
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java21
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java35
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimationSpec.java35
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java40
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java126
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java70
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java36
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java32
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java103
-rw-r--r--services/incremental/IncrementalService.cpp7
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java84
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java213
-rw-r--r--services/tests/servicestests/AndroidManifest.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java452
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java168
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java3
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java38
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java53
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java26
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt113
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt169
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml11
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java33
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java32
-rw-r--r--tests/TrustTests/Android.bp2
-rw-r--r--tests/TrustTests/src/android/trust/BaseTrustAgentService.kt2
-rw-r--r--tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt16
-rw-r--r--tests/TrustTests/src/android/trust/test/LockUserTest.kt1
-rw-r--r--tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt47
-rw-r--r--tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt37
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt22
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt47
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt13
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/utils.kt51
-rw-r--r--tools/locked_region_code_injection/Android.bp8
-rw-r--r--tools/sdkparcelables/Android.bp2
-rw-r--r--tools/traceinjection/Android.bp8
333 files changed, 9239 insertions, 4817 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 88082f7dfa4b..dd0d07f68547 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -284,9 +284,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
/** @hide */
- public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 28800;
+ public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 4000;
/** @hide */
- public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 52000;
+ public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 28_800;
// TODO: add AlarmManager modifier default values
/** @hide */
public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
@@ -373,9 +373,9 @@ public class EconomyManager {
/** @hide */
public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
/** @hide */
- public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 460_000;
+ public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 100_000;
/** @hide */
- public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 900_000;
+ public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 460_000;
// TODO: add JobScheduler modifier default values
/** @hide */
public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
new file mode 100644
index 000000000000..993e178de0ee
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
@@ -0,0 +1,407 @@
+/*
+ * 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.server.job;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Pools;
+import android.util.SparseArray;
+
+import com.android.server.job.controllers.JobStatus;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+
+/**
+ * A utility class to maintain a sorted list of currently pending jobs. The sorting system is
+ * modeled after topological sort, so the returned order may not always be consistent.
+ */
+class PendingJobQueue {
+ private final Pools.Pool<AppJobQueue> mAppJobQueuePool = new Pools.SimplePool<>(8);
+
+ /** Set of currently used queues, keyed by source UID. */
+ private final SparseArray<AppJobQueue> mCurrentQueues = new SparseArray<>();
+ /**
+ * Same set of AppJobQueues as in {@link #mCurrentQueues}, but ordered by the next timestamp
+ * to make iterating through the job list faster.
+ */
+ private final PriorityQueue<AppJobQueue> mOrderedQueues = new PriorityQueue<>(
+ (ajq1, ajq2) -> {
+ final long t1 = ajq1.peekNextTimestamp();
+ final long t2 = ajq2.peekNextTimestamp();
+ if (t1 == AppJobQueue.NO_NEXT_TIMESTAMP) {
+ if (t2 == AppJobQueue.NO_NEXT_TIMESTAMP) {
+ return 0;
+ }
+ return 1;
+ } else if (t2 == AppJobQueue.NO_NEXT_TIMESTAMP) {
+ return -1;
+ }
+ return Long.compare(t1, t2);
+ });
+
+ private int mSize = 0;
+
+ private boolean mNeedToResetIterators = false;
+
+ void add(@NonNull JobStatus job) {
+ final AppJobQueue ajq = getAppJobQueue(job.getSourceUid(), true);
+ final long prevTimestamp = ajq.peekNextTimestamp();
+ ajq.add(job);
+ mSize++;
+ if (prevTimestamp != ajq.peekNextTimestamp()) {
+ mOrderedQueues.remove(ajq);
+ mOrderedQueues.offer(ajq);
+ }
+ }
+
+ void addAll(@NonNull List<JobStatus> jobs) {
+ final SparseArray<List<JobStatus>> jobsByUid = new SparseArray<>();
+ for (int i = jobs.size() - 1; i >= 0; --i) {
+ final JobStatus job = jobs.get(i);
+ List<JobStatus> appJobs = jobsByUid.get(job.getSourceUid());
+ if (appJobs == null) {
+ appJobs = new ArrayList<>();
+ jobsByUid.put(job.getSourceUid(), appJobs);
+ }
+ appJobs.add(job);
+ }
+ for (int i = jobsByUid.size() - 1; i >= 0; --i) {
+ final AppJobQueue ajq = getAppJobQueue(jobsByUid.keyAt(i), true);
+ ajq.addAll(jobsByUid.valueAt(i));
+ }
+ mSize += jobs.size();
+ mOrderedQueues.clear();
+ }
+
+ void clear() {
+ mSize = 0;
+ for (int i = mCurrentQueues.size() - 1; i >= 0; --i) {
+ final AppJobQueue ajq = mCurrentQueues.valueAt(i);
+ ajq.clear();
+ mAppJobQueuePool.release(ajq);
+ }
+ mCurrentQueues.clear();
+ mOrderedQueues.clear();
+ }
+
+ boolean contains(@NonNull JobStatus job) {
+ final AppJobQueue ajq = mCurrentQueues.get(job.getSourceUid());
+ if (ajq == null) {
+ return false;
+ }
+ return ajq.contains(job);
+ }
+
+ private AppJobQueue getAppJobQueue(int uid, boolean create) {
+ AppJobQueue ajq = mCurrentQueues.get(uid);
+ if (ajq == null && create) {
+ ajq = mAppJobQueuePool.acquire();
+ if (ajq == null) {
+ ajq = new AppJobQueue();
+ }
+ mCurrentQueues.put(uid, ajq);
+ }
+ return ajq;
+ }
+
+ @Nullable
+ JobStatus next() {
+ if (mNeedToResetIterators) {
+ mOrderedQueues.clear();
+ for (int i = mCurrentQueues.size() - 1; i >= 0; --i) {
+ final AppJobQueue ajq = mCurrentQueues.valueAt(i);
+ ajq.resetIterator(0);
+ mOrderedQueues.offer(ajq);
+ }
+ mNeedToResetIterators = false;
+ } else if (mOrderedQueues.size() == 0) {
+ for (int i = mCurrentQueues.size() - 1; i >= 0; --i) {
+ final AppJobQueue ajq = mCurrentQueues.valueAt(i);
+ mOrderedQueues.offer(ajq);
+ }
+ }
+ final AppJobQueue earliestQueue = mOrderedQueues.poll();
+ if (earliestQueue != null) {
+ JobStatus job = earliestQueue.next();
+ mOrderedQueues.offer(earliestQueue);
+ return job;
+ }
+ return null;
+ }
+
+ boolean remove(@NonNull JobStatus job) {
+ final AppJobQueue ajq = getAppJobQueue(job.getSourceUid(), false);
+ if (ajq == null) {
+ return false;
+ }
+
+ final long prevTimestamp = ajq.peekNextTimestamp();
+ if (!ajq.remove(job)) {
+ return false;
+ }
+
+ mSize--;
+ if (ajq.size() == 0) {
+ mCurrentQueues.remove(job.getSourceUid());
+ mOrderedQueues.remove(ajq);
+ ajq.clear();
+ mAppJobQueuePool.release(ajq);
+ } else if (prevTimestamp != ajq.peekNextTimestamp()) {
+ mOrderedQueues.remove(ajq);
+ mOrderedQueues.offer(ajq);
+ }
+
+ return true;
+ }
+
+ /** Resets the iterating index to the front of the queue. */
+ void resetIterator() {
+ // Lazily reset the iterating indices (avoid looping through all the current queues until
+ // absolutely necessary).
+ mNeedToResetIterators = true;
+ }
+
+ int size() {
+ return mSize;
+ }
+
+ private static final class AppJobQueue {
+ static final long NO_NEXT_TIMESTAMP = -1L;
+
+ private static class AdjustedJobStatus {
+ public long adjustedEnqueueTime;
+ public JobStatus job;
+
+ void clear() {
+ adjustedEnqueueTime = 0;
+ job = null;
+ }
+ }
+
+ private static final Comparator<AdjustedJobStatus> sJobComparator = (aj1, aj2) -> {
+ if (aj1 == aj2) {
+ return 0;
+ }
+ final JobStatus job1 = aj1.job;
+ final JobStatus job2 = aj2.job;
+ // Jobs with an override state set (via adb) should be put first as tests/developers
+ // expect the jobs to run immediately.
+ if (job1.overrideState != job2.overrideState) {
+ // Higher override state (OVERRIDE_FULL) should be before lower state
+ // (OVERRIDE_SOFT)
+ return job2.overrideState - job1.overrideState;
+ }
+
+ final boolean job1EJ = job1.isRequestedExpeditedJob();
+ final boolean job2EJ = job2.isRequestedExpeditedJob();
+ if (job1EJ != job2EJ) {
+ // Attempt to run requested expedited jobs ahead of regular jobs, regardless of
+ // expedited job quota.
+ return job1EJ ? -1 : 1;
+ }
+
+ final int job1Priority = job1.getEffectivePriority();
+ final int job2Priority = job2.getEffectivePriority();
+ if (job1Priority != job2Priority) {
+ // Use the priority set by an app for intra-app job ordering. Higher
+ // priority should be before lower priority.
+ return job2Priority - job1Priority;
+ }
+
+ if (job1.lastEvaluatedBias != job2.lastEvaluatedBias) {
+ // Higher bias should go first.
+ return job2.lastEvaluatedBias - job1.lastEvaluatedBias;
+ }
+
+ if (job1.enqueueTime < job2.enqueueTime) {
+ return -1;
+ }
+ return job1.enqueueTime > job2.enqueueTime ? 1 : 0;
+ };
+
+ private static final Pools.Pool<AdjustedJobStatus> mAdjustedJobStatusPool =
+ new Pools.SimplePool<>(16);
+
+ private final List<AdjustedJobStatus> mJobs = new ArrayList<>();
+ private int mCurIndex = 0;
+
+ void add(@NonNull JobStatus jobStatus) {
+ AdjustedJobStatus adjustedJobStatus = mAdjustedJobStatusPool.acquire();
+ if (adjustedJobStatus == null) {
+ adjustedJobStatus = new AdjustedJobStatus();
+ }
+ adjustedJobStatus.adjustedEnqueueTime = jobStatus.enqueueTime;
+ adjustedJobStatus.job = jobStatus;
+
+ int where = Collections.binarySearch(mJobs, adjustedJobStatus, sJobComparator);
+ if (where < 0) {
+ where = ~where;
+ }
+ mJobs.add(where, adjustedJobStatus);
+ if (where < mCurIndex) {
+ // Shift the current index back to make sure the new job is evaluated on the next
+ // iteration.
+ mCurIndex = where;
+ }
+
+ if (where > 0) {
+ final long prevTimestamp = mJobs.get(where - 1).adjustedEnqueueTime;
+ adjustedJobStatus.adjustedEnqueueTime =
+ Math.max(prevTimestamp, adjustedJobStatus.adjustedEnqueueTime);
+ }
+ final int numJobs = mJobs.size();
+ if (where < numJobs - 1) {
+ // Potentially need to adjust following job timestamps as well.
+ for (int i = where; i < numJobs; ++i) {
+ final AdjustedJobStatus ajs = mJobs.get(i);
+ if (adjustedJobStatus.adjustedEnqueueTime < ajs.adjustedEnqueueTime) {
+ // No further need to adjust.
+ break;
+ }
+ ajs.adjustedEnqueueTime = adjustedJobStatus.adjustedEnqueueTime;
+ }
+ }
+ }
+
+ void addAll(@NonNull List<JobStatus> jobs) {
+ int earliestIndex = Integer.MAX_VALUE;
+
+ for (int i = jobs.size() - 1; i >= 0; --i) {
+ final JobStatus job = jobs.get(i);
+
+ AdjustedJobStatus adjustedJobStatus = mAdjustedJobStatusPool.acquire();
+ if (adjustedJobStatus == null) {
+ adjustedJobStatus = new AdjustedJobStatus();
+ }
+ adjustedJobStatus.adjustedEnqueueTime = job.enqueueTime;
+ adjustedJobStatus.job = job;
+
+ int where = Collections.binarySearch(mJobs, adjustedJobStatus, sJobComparator);
+ if (where < 0) {
+ where = ~where;
+ }
+ mJobs.add(where, adjustedJobStatus);
+ if (where < mCurIndex) {
+ // Shift the current index back to make sure the new job is evaluated on the
+ // next iteration.
+ mCurIndex = where;
+ }
+ earliestIndex = Math.min(earliestIndex, where);
+ }
+
+ final int numJobs = mJobs.size();
+ for (int i = Math.max(earliestIndex, 1); i < numJobs; ++i) {
+ final AdjustedJobStatus ajs = mJobs.get(i);
+ final AdjustedJobStatus prev = mJobs.get(i - 1);
+ ajs.adjustedEnqueueTime =
+ Math.max(ajs.adjustedEnqueueTime, prev.adjustedEnqueueTime);
+ }
+ }
+
+ void clear() {
+ mJobs.clear();
+ mCurIndex = 0;
+ }
+
+ boolean contains(@NonNull JobStatus job) {
+ return indexOf(job) >= 0;
+ }
+
+ private int indexOf(@NonNull JobStatus jobStatus) {
+ AdjustedJobStatus adjustedJobStatus = mAdjustedJobStatusPool.acquire();
+ if (adjustedJobStatus == null) {
+ adjustedJobStatus = new AdjustedJobStatus();
+ }
+ adjustedJobStatus.adjustedEnqueueTime = jobStatus.enqueueTime;
+ adjustedJobStatus.job = jobStatus;
+
+ int where = Collections.binarySearch(mJobs, adjustedJobStatus, sJobComparator);
+ adjustedJobStatus.clear();
+ mAdjustedJobStatusPool.release(adjustedJobStatus);
+ return where;
+ }
+
+ @Nullable
+ JobStatus next() {
+ if (mCurIndex >= mJobs.size()) {
+ return null;
+ }
+ JobStatus next = mJobs.get(mCurIndex).job;
+ mCurIndex++;
+ return next;
+ }
+
+ long peekNextTimestamp() {
+ if (mCurIndex >= mJobs.size()) {
+ return NO_NEXT_TIMESTAMP;
+ }
+ return mJobs.get(mCurIndex).adjustedEnqueueTime;
+ }
+
+ boolean remove(@NonNull JobStatus jobStatus) {
+ final int idx = indexOf(jobStatus);
+ if (idx < 0) {
+ // Doesn't exist...
+ return false;
+ }
+ final AdjustedJobStatus adjustedJobStatus = mJobs.remove(idx);
+ adjustedJobStatus.clear();
+ mAdjustedJobStatusPool.release(adjustedJobStatus);
+ if (idx < mCurIndex) {
+ mCurIndex--;
+ }
+ return true;
+ }
+
+ /**
+ * Resets the internal index to point to the first JobStatus whose adjusted time is equal to
+ * or after the given timestamp.
+ */
+ void resetIterator(long earliestEnqueueTime) {
+ if (earliestEnqueueTime == 0 || mJobs.size() == 0) {
+ mCurIndex = 0;
+ return;
+ }
+
+ // Binary search
+ int low = 0;
+ int high = mJobs.size() - 1;
+
+ while (low < high) {
+ int mid = (low + high) >>> 1;
+ AdjustedJobStatus midVal = mJobs.get(mid);
+
+ if (midVal.adjustedEnqueueTime < earliestEnqueueTime) {
+ low = mid + 1;
+ } else if (midVal.adjustedEnqueueTime > earliestEnqueueTime) {
+ high = mid - 1;
+ } else {
+ high = mid;
+ }
+ }
+ mCurIndex = high;
+ }
+
+ int size() {
+ return mJobs.size();
+ }
+ }
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 37022d0ab402..5022f4ab11db 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1755,7 +1755,7 @@ package android {
field public static final int windowShowWallpaper = 16843410; // 0x1010292
field public static final int windowSoftInputMode = 16843307; // 0x101022b
field public static final int windowSplashScreenAnimatedIcon = 16844333; // 0x101062d
- field public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e
+ field @Deprecated public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e
field public static final int windowSplashScreenBackground = 16844332; // 0x101062c
field public static final int windowSplashScreenBehavior;
field public static final int windowSplashScreenBrandingImage = 16844335; // 0x101062f
@@ -44820,7 +44820,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);
+ 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, @Nullable android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public void ellipsized(int, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
@@ -44838,9 +44838,9 @@ package android.text {
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 @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, @Nullable 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 @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, @Nullable 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);
}
@@ -52262,7 +52262,7 @@ package android.view.animation {
method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
method public long computeDurationHint();
method protected void ensureInterpolator();
- method @Deprecated @ColorInt public int getBackgroundColor();
+ method @ColorInt public int getBackgroundColor();
method @Deprecated public boolean getDetachWallpaper();
method public long getDuration();
method public boolean getFillAfter();
@@ -52287,7 +52287,7 @@ package android.view.animation {
method public void restrictDuration(long);
method public void scaleCurrentDuration(float);
method public void setAnimationListener(android.view.animation.Animation.AnimationListener);
- method @Deprecated public void setBackgroundColor(@ColorInt int);
+ method public void setBackgroundColor(@ColorInt int);
method @Deprecated public void setDetachWallpaper(boolean);
method public void setDuration(long);
method public void setFillAfter(boolean);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a748142d65e..dd4d4dcd3d03 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -403,6 +403,7 @@ package android {
public static final class R.drawable {
field public static final int ic_info = 17301684; // 0x10800b4
+ field public static final int ic_safety_protection;
}
public static final class R.raw {
@@ -438,6 +439,7 @@ package android {
field public static final int config_systemContacts = 17039403; // 0x104002b
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
+ field public static final int config_systemSettingsIntelligence;
field public static final int config_systemShell = 17039402; // 0x104002a
field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e
field public static final int config_systemSupervision;
@@ -448,6 +450,7 @@ package android {
field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
field public static final int config_systemWellbeing = 17039408; // 0x1040030
field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
+ field public static final int safety_protection_display_text;
}
public static final class R.style {
@@ -9802,8 +9805,8 @@ package android.os {
method public boolean isRestrictedProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isUserOfType(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
@@ -10082,10 +10085,10 @@ package android.permission {
public final class PermissionManager {
method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
- method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
- method public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+ method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+ method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
- method public int checkPermissionForStartDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+ method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForStartDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
method public void finishDataDelivery(@NonNull String, @NonNull android.content.AttributionSource);
method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
@@ -11802,11 +11805,23 @@ package android.service.translation {
package android.service.trust {
+ public final class GrantTrustResult implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getStatus();
+ method @NonNull public static String statusToString(int);
+ method @NonNull public static android.service.trust.GrantTrustResult withStatus(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.trust.GrantTrustResult> CREATOR;
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ field public static final int STATUS_UNLOCKED_BY_GRANT = 1; // 0x1
+ }
+
public class TrustAgentService extends android.app.Service {
ctor public TrustAgentService();
method public final void addEscrowToken(byte[], android.os.UserHandle);
method @Deprecated public final void grantTrust(CharSequence, long, boolean);
- method public final void grantTrust(CharSequence, long, int);
+ method @Deprecated public final void grantTrust(CharSequence, long, int);
+ method public final void grantTrust(@NonNull CharSequence, long, int, @Nullable java.util.function.Consumer<android.service.trust.GrantTrustResult>);
method public final void isEscrowTokenActive(long, android.os.UserHandle);
method public final void lockUser();
method public final android.os.IBinder onBind(android.content.Intent);
@@ -11819,7 +11834,8 @@ package android.service.trust {
method public void onEscrowTokenStateReceived(long, int);
method public void onTrustTimeout();
method public void onUnlockAttempt(boolean);
- method public void onUserRequestedUnlock();
+ method public void onUserMayRequestUnlock();
+ method public void onUserRequestedUnlock(boolean);
method public final void removeEscrowToken(long, android.os.UserHandle);
method public final void revokeTrust();
method public final void setManagingTrust(boolean);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e8c903fc2f13..5b05b45d05db 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4475,23 +4475,6 @@ public class ActivityManager {
}
/**
- * Logs out current current foreground user by switching to the system user and stopping the
- * user being switched from.
- * @hide
- */
- public static void logoutCurrentUser() {
- int currentUser = ActivityManager.getCurrentUser();
- if (currentUser != UserHandle.USER_SYSTEM) {
- try {
- getService().switchUser(UserHandle.USER_SYSTEM);
- getService().stopUser(currentUser, /* force= */ false, null);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
* Stops the given {@code userId}.
*
* @hide
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 0178fa143445..0d8103675cc2 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1162,6 +1162,7 @@ public class ActivityOptions extends ComponentOptions {
case ANIM_CUSTOM:
mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+ mCustomBackgroundColor = opts.getInt(KEY_ANIM_BACKGROUND_COLOR, 0);
mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
opts.getBinder(KEY_ANIM_START_LISTENER));
break;
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index b170aa2a1325..b90408d4395e 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -517,14 +517,15 @@ public class SecurityLog {
public static final int TAG_PASSWORD_CHANGED = SecurityLogTags.SECURITY_PASSWORD_CHANGED;
/**
- * Indicates that the device attempts to connect to a WiFi network.
- * The log entry contains the following information about the
+ * Indicates that an event occurred as the device attempted to connect to
+ * a WiFi network. The log entry contains the following information about the
* event, encapsulated in an {@link Object} array and accessible via
* {@link SecurityEvent#getData()}:
- * <li> [0] The SSID of the network ({@code String})
- * <li> [1] The BSSID of the network ({@code String})
- * <li> [2] Whether the connection is successful ({@code Integer}, 1 if successful, 0 otherwise)
- * <li> [3] Optional human-readable failure reason, empty string if none ({@code String})
+ * <li> [0] Last 2 octets of the network BSSID ({@code String}, in the form "xx:xx:xx:xx:AA:BB")
+ * <li> [1] Type of event that occurred ({@code String}). Event types are CONNECTED,
+ * DISCONNECTED, ASSOCIATING, ASSOCIATED, EAP_METHOD_SELECTED, EAP_FAILURE,
+ * SSID_TEMP_DISABLED, and OPEN_SSL_FAILURE.
+ * <li> [2] Optional human-readable failure reason, empty string if none ({@code String})
*/
public static final int TAG_WIFI_CONNECTION = SecurityLogTags.SECURITY_WIFI_CONNECTION;
@@ -533,9 +534,8 @@ public class SecurityLog {
* The log entry contains the following information about the
* event, encapsulated in an {@link Object} array and accessible via
* {@link SecurityEvent#getData()}:
- * <li> [0] The SSID of the connected network ({@code String})
- * <li> [1] The BSSID of the connected network ({@code String})
- * <li> [2] Optional human-readable disconnection reason, empty string if none ({@code String})
+ * <li> [0] Last 2 octets of the network BSSID ({@code String}, in the form "xx:xx:xx:xx:AA:BB")
+ * <li> [1] Optional human-readable disconnection reason, empty string if none ({@code String})
*/
public static final int TAG_WIFI_DISCONNECTION = SecurityLogTags.SECURITY_WIFI_DISCONNECTION;
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index 5f41109600b2..b06e5a5849fd 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -41,7 +41,7 @@ option java_package android.app.admin
210034 security_camera_policy_set (package|3),(admin_user|1),(target_user|1),(disabled|1)
210035 security_password_complexity_required (package|3),(admin_user|1),(target_user|1),(complexity|1)
210036 security_password_changed (password_complexity|1),(target_user|1)
-210037 security_wifi_connection (ssid|3),(bssid|3),(success|1),(reason|3)
-210038 security_wifi_disconnection (ssid|3),(bssid|3),(reason|3)
+210037 security_wifi_connection (bssid|3),(event_type|3),(reason|3)
+210038 security_wifi_disconnection (bssid|3),(reason|3)
210039 security_bluetooth_connection (addr|3),(success|1),(reason|3)
210040 security_bluetooth_disconnection (addr|3),(reason|3) \ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index b786444faa8c..45146fd95104 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,7 +26,8 @@ import android.hardware.biometrics.BiometricSourceType;
*/
interface ITrustManager {
void reportUnlockAttempt(boolean successful, int userId);
- void reportUserRequestedUnlock(int userId);
+ void reportUserRequestedUnlock(int userId, boolean dismissKeyguard);
+ void reportUserMayRequestUnlock(int userId);
void reportUnlockLockout(int timeoutMs, int userId);
void reportEnabledTrustAgentsChanged(int userId);
void registerTrustListener(in ITrustListener trustListener);
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 70b7de0767e4..9e825b7207e0 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -92,10 +92,25 @@ public class TrustManager {
* Reports that the user {@code userId} is likely interested in unlocking the device.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @param dismissKeyguard whether the user wants to dismiss keyguard
+ */
+ public void reportUserRequestedUnlock(int userId, boolean dismissKeyguard) {
+ try {
+ mService.reportUserRequestedUnlock(userId, dismissKeyguard);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports that the user {@code userId} may want to unlock the device soon.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*/
- public void reportUserRequestedUnlock(int userId) {
+ public void reportUserMayRequestUnlock(int userId) {
try {
- mService.reportUserRequestedUnlock(userId);
+ mService.reportUserMayRequestUnlock(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 15685000d6ba..56939f0ae650 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -76,6 +76,56 @@ public final class CompanionDeviceManager {
private static final String LOG_TAG = "CompanionDeviceManager";
/**
+ * The result code to propagate back to the originating activity, indicates the association
+ * dialog is explicitly declined by the users.
+ *
+ * @hide
+ */
+ public static final int RESULT_USER_REJECTED = 1;
+
+ /**
+ * The result code to propagate back to the originating activity, indicates the association
+ * dialog is dismissed if there's no device found after 20 seconds.
+ *
+ * @hide
+ */
+ public static final int RESULT_DISCOVERY_TIMEOUT = 2;
+
+ /**
+ * The result code to propagate back to the originating activity, indicates the internal error
+ * in CompanionDeviceManager.
+ *
+ * @hide
+ */
+ public static final int RESULT_INTERNAL_ERROR = 3;
+
+ /**
+ * Requesting applications will receive the String in {@link Callback#onFailure} if the
+ * association dialog is explicitly declined by the users. e.g. press the Don't allow button.
+ *
+ * @hide
+ */
+ public static final String REASON_USER_REJECTED = "user_rejected";
+
+ /**
+ * Requesting applications will receive the String in {@link Callback#onFailure} if there's
+ * no device found after 20 seconds.
+ *
+ * @hide
+ */
+ public static final String REASON_DISCOVERY_TIMEOUT = "discovery_timeout";
+
+ /**
+ * Requesting applications will receive the String in {@link Callback#onFailure} if the
+ * association dialog is in-explicitly declined by the users. e.g. phone is locked, switch to
+ * another app or press outside the dialog.
+ *
+ * @hide
+ */
+ public static final String REASON_CANCELED = "canceled";
+
+
+ /**
* A device, returned in the activity result of the {@link IntentSender} received in
* {@link Callback#onDeviceFound}
*
diff --git a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
index df13ade2bf5e..bd25b8f2ad88 100644
--- a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
+++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
@@ -16,9 +16,9 @@
package android.hardware.location;
+import android.os.BadParcelableException;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
/**
* Geofence Hardware Request used for internal location services communication.
@@ -139,11 +139,8 @@ public final class GeofenceHardwareRequestParcelable implements Parcelable {
@Override
public GeofenceHardwareRequestParcelable createFromParcel(Parcel parcel) {
int geofenceType = parcel.readInt();
- if(geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
- Log.e(
- "GeofenceHardwareRequest",
- String.format("Invalid Geofence type: %d", geofenceType));
- return null;
+ if (geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
+ throw new BadParcelableException("Invalid Geofence type: " + geofenceType);
}
GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 02302a20fe38..f9ed0e3db499 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -81,6 +81,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_CAN_START_STYLUS_HANDWRITING = 100;
private static final int DO_START_STYLUS_HANDWRITING = 110;
private static final int DO_INIT_INK_WINDOW = 120;
+ private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -263,6 +264,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputMethod.initInkWindow();
return;
}
+ case DO_FINISH_STYLUS_HANDWRITING: {
+ inputMethod.finishStylusHandwriting();
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
@@ -427,4 +432,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void initInkWindow() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_INIT_INK_WINDOW));
}
+
+ @BinderThread
+ @Override
+ public void finishStylusHandwriting() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_STYLUS_HANDWRITING));
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b46bb3257c86..4fdd53425328 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -994,6 +994,15 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void finishStylusHandwriting() {
+ InputMethodService.this.finishStylusHandwriting();
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
@@ -2461,7 +2470,7 @@ public class InputMethodService extends AbstractInputMethodService {
mHandwritingEventReceiver = null;
mInkWindow.hide(false /* remove */);
- mPrivOps.finishStylusHandwriting(requestId);
+ mPrivOps.resetStylusHandwriting(requestId);
mOnPreparedStylusHwCalled = false;
onFinishStylusHandwriting();
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1c85f692b232..42e6ac4df8af 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -406,7 +406,7 @@ public class Build {
public static final String CODENAME = getString("ro.build.version.codename");
/**
- * All known codenames starting from {@link VERSION_CODES.Q}.
+ * All known codenames that are present in {@link VERSION_CODES}.
*
* <p>This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL"
* then the value of that is present in this set.
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 043e688489a4..a4f6004a288a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -107,9 +107,6 @@ public final class Trace {
public static final long TRACE_TAG_RRO = 1L << 26;
/** @hide */
public static final long TRACE_TAG_THERMAL = 1L << 27;
- /** @hide */
-
- public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 608c424451b7..c4cb3195e485 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2186,9 +2186,17 @@ public class UserManager {
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
- @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS,
+ android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS})
public boolean isUserNameSet() {
try {
return mService.isUserNameSet(getContextUserIfAppropriate());
@@ -2292,8 +2300,11 @@ public class UserManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS})
@UserHandleAware
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isUserOfType(@NonNull String userType) {
try {
return mService.isUserOfType(mUserId, userType);
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 642c618ba1f7..5bed32cb0438 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -167,11 +167,27 @@ public final class VibrationAttributes implements Parcelable {
public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 0x2;
/**
+ * Flag requesting vibration effect to be played with fresh user settings values.
+ *
+ * <p>This flag is not protected by any permission, but vibrations that use it require an extra
+ * query of user vibration intensity settings, ringer mode and other controls that affect the
+ * vibration effect playback, which can increase the latency for the overall request.
+ *
+ * <p>This is intended to be used on scenarios where the user settings might have changed
+ * recently, and needs to be applied to this vibration, like settings controllers that preview
+ * newly set intensities to the user.
+ *
+ * @hide
+ */
+ public static final int FLAG_INVALIDATE_SETTINGS_CACHE = 0x3;
+
+ /**
* All flags supported by vibrator service, update it when adding new flag.
* @hide
*/
public static final int FLAG_ALL_SUPPORTED =
- FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+ FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+ | FLAG_INVALIDATE_SETTINGS_CACHE;
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
@@ -446,8 +462,10 @@ public final class VibrationAttributes implements Parcelable {
}
/**
- * Set flags
- * @param flags combination of flags to be set.
+ * Sets only the flags specified in the bitmask, leaving the other supported flag values
+ * unchanged in the builder.
+ *
+ * @param flags Combination of flags to be set.
* @param mask Bit range that should be changed.
* @return the same Builder instance.
*/
@@ -456,5 +474,18 @@ public final class VibrationAttributes implements Parcelable {
mFlags = (mFlags & ~mask) | (flags & mask);
return this;
}
+
+ /**
+ * Set all supported flags with given combination of flags, overriding any previous values
+ * set to this builder.
+ *
+ * @param flags combination of flags to be set.
+ * @return the same Builder instance.
+ *
+ * @hide
+ */
+ public @NonNull Builder setFlags(@Flag int flags) {
+ return setFlags(flags, FLAG_ALL_SUPPORTED);
+ }
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9971cbcfa586..646a7095c1b3 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -301,28 +301,7 @@ public class StorageManager {
/** @hide The volume is not encrypted. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int ENCRYPTION_STATE_NONE =
- IVold.ENCRYPTION_STATE_NONE;
-
- /** @hide The volume has been encrypted succesfully. */
- public static final int ENCRYPTION_STATE_OK =
- IVold.ENCRYPTION_STATE_OK;
-
- /** @hide The volume is in a bad state. */
- public static final int ENCRYPTION_STATE_ERROR_UNKNOWN =
- IVold.ENCRYPTION_STATE_ERROR_UNKNOWN;
-
- /** @hide Encryption is incomplete */
- public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE =
- IVold.ENCRYPTION_STATE_ERROR_INCOMPLETE;
-
- /** @hide Encryption is incomplete and irrecoverable */
- public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT =
- IVold.ENCRYPTION_STATE_ERROR_INCONSISTENT;
-
- /** @hide Underlying data is corrupt */
- public static final int ENCRYPTION_STATE_ERROR_CORRUPT =
- IVold.ENCRYPTION_STATE_ERROR_CORRUPT;
+ public static final int ENCRYPTION_STATE_NONE = 1;
private static volatile IStorageManager sStorageManager = null;
@@ -3033,15 +3012,10 @@ public class StorageManager {
@GuardedBy("mFuseAppLoopLock")
private @Nullable FuseAppLoop mFuseAppLoop = null;
- /// Consts to match the password types in cryptfs.h
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int CRYPT_TYPE_PASSWORD = IVold.PASSWORD_TYPE_PASSWORD;
+ public static final int CRYPT_TYPE_PASSWORD = 0;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int CRYPT_TYPE_DEFAULT = IVold.PASSWORD_TYPE_DEFAULT;
- /** @hide */
- public static final int CRYPT_TYPE_PATTERN = IVold.PASSWORD_TYPE_PATTERN;
- /** @hide */
- public static final int CRYPT_TYPE_PIN = IVold.PASSWORD_TYPE_PIN;
+ public static final int CRYPT_TYPE_DEFAULT = 1;
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 7a797ce28870..69728756d745 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -250,6 +250,10 @@ public final class PermissionManager {
* will evaluate the permission access based on the current fg/bg state of the app and
* leave a record that the data was accessed.
*
+ * <p>Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS
+ * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the
+ * accesses will have the UNTRUSTED flags.
+ *
* @param permission The permission to check.
* @param attributionSource the permission identity
* @param message A message describing the reason the permission was checked
@@ -259,6 +263,7 @@ public final class PermissionManager {
* @see #checkPermissionForPreflight(String, AttributionSource)
*/
@PermissionCheckerManager.PermissionResult
+ @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true)
public int checkPermissionForDataDelivery(@NonNull String permission,
@NonNull AttributionSource attributionSource, @Nullable String message) {
return PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
@@ -278,9 +283,14 @@ public final class PermissionManager {
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*
+ * <p>Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS
+ * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the
+ * accesses will have the UNTRUSTED flags.
+ *
* @see #checkPermissionForDataDelivery(String, AttributionSource, String)
*/
@PermissionCheckerManager.PermissionResult
+ @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true)
public int checkPermissionForStartDataDelivery(@NonNull String permission,
@NonNull AttributionSource attributionSource, @Nullable String message) {
return PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
@@ -320,6 +330,10 @@ public final class PermissionManager {
* will evaluate the permission access based on the current fg/bg state of the app and
* leave a record that the data was accessed.
*
+ * <p>Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS
+ * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the
+ * accesses will have the UNTRUSTED flags.
+ *
* @param permission The permission to check.
* @param attributionSource the permission identity
* @param message A message describing the reason the permission was checked
@@ -329,6 +343,7 @@ public final class PermissionManager {
* @see #checkPermissionForPreflight(String, AttributionSource)
*/
@PermissionCheckerManager.PermissionResult
+ @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true)
public int checkPermissionForDataDeliveryFromDataSource(@NonNull String permission,
@NonNull AttributionSource attributionSource, @Nullable String message) {
return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(mContext, permission,
diff --git a/core/java/android/service/trust/GrantTrustResult.aidl b/core/java/android/service/trust/GrantTrustResult.aidl
new file mode 100644
index 000000000000..d24a6bc62f17
--- /dev/null
+++ b/core/java/android/service/trust/GrantTrustResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.service.trust;
+
+parcelable GrantTrustResult;
diff --git a/core/java/android/service/trust/GrantTrustResult.java b/core/java/android/service/trust/GrantTrustResult.java
new file mode 100644
index 000000000000..7cf708a3d6e3
--- /dev/null
+++ b/core/java/android/service/trust/GrantTrustResult.java
@@ -0,0 +1,179 @@
+/*
+ * 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.service.trust;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result type for a callback in a call to
+ * {@link TrustAgentService#grantTrust(CharSequence, long, int)}.
+ *
+ * @hide
+ */
+@DataClass(genHiddenConstructor = true)
+@SystemApi
+public final class GrantTrustResult implements Parcelable {
+
+ /** Result status is unknown to this version of the SDK. */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /** The device went from locked to unlocked as a result of the call. */
+ public static final int STATUS_UNLOCKED_BY_GRANT = 1;
+
+ @Status
+ private int mStatus;
+
+ /** Returns a new {@link GrantTrustResult} with the specified status. */
+ @NonNull
+ public static GrantTrustResult withStatus(@Status int status) {
+ return new GrantTrustResult(status);
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/trust/GrantTrustResult.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "STATUS_", value = {
+ STATUS_UNKNOWN,
+ STATUS_UNLOCKED_BY_GRANT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Status {}
+
+ @NonNull
+ @DataClass.Generated.Member
+ public static String statusToString(@Status int value) {
+ switch (value) {
+ case STATUS_UNKNOWN:
+ return "STATUS_UNKNOWN";
+ case STATUS_UNLOCKED_BY_GRANT:
+ return "STATUS_UNLOCKED_BY_GRANT";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ /**
+ * Creates a new GrantTrustResult.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public GrantTrustResult(
+ @Status int status) {
+ this.mStatus = status;
+
+ if (!(mStatus == STATUS_UNKNOWN)
+ && !(mStatus == STATUS_UNLOCKED_BY_GRANT)) {
+ throw new java.lang.IllegalArgumentException(
+ "status was " + mStatus + " but must be one of: "
+ + "STATUS_UNKNOWN(" + STATUS_UNKNOWN + "), "
+ + "STATUS_UNLOCKED_BY_GRANT(" + STATUS_UNLOCKED_BY_GRANT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @Status int getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mStatus);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ GrantTrustResult(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int status = in.readInt();
+
+ this.mStatus = status;
+
+ if (!(mStatus == STATUS_UNKNOWN)
+ && !(mStatus == STATUS_UNLOCKED_BY_GRANT)) {
+ throw new java.lang.IllegalArgumentException(
+ "status was " + mStatus + " but must be one of: "
+ + "STATUS_UNKNOWN(" + STATUS_UNKNOWN + "), "
+ + "STATUS_UNLOCKED_BY_GRANT(" + STATUS_UNLOCKED_BY_GRANT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<GrantTrustResult> CREATOR
+ = new Parcelable.Creator<GrantTrustResult>() {
+ @Override
+ public GrantTrustResult[] newArray(int size) {
+ return new GrantTrustResult[size];
+ }
+
+ @Override
+ public GrantTrustResult createFromParcel(@NonNull android.os.Parcel in) {
+ return new GrantTrustResult(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1647878197834L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/trust/GrantTrustResult.java",
+ inputSignatures = "public static final int STATUS_UNKNOWN\npublic static final int STATUS_UNLOCKED_BY_GRANT\nprivate @android.service.trust.GrantTrustResult.Status int mStatus\npublic static @android.annotation.NonNull android.service.trust.GrantTrustResult withStatus(int)\nclass GrantTrustResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index ec3b8575ed36..70c29a682cd1 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,7 +25,8 @@ import android.service.trust.ITrustAgentServiceCallback;
*/
interface ITrustAgentService {
oneway void onUnlockAttempt(boolean successful);
- oneway void onUserRequestedUnlock();
+ oneway void onUserRequestedUnlock(boolean dismissKeyguard);
+ oneway void onUserMayRequestUnlock();
oneway void onUnlockLockout(int timeoutMs);
oneway void onTrustTimeout();
oneway void onDeviceLocked();
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index 6b11e7463abc..e9e40c0175ac 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -18,13 +18,15 @@ package android.service.trust;
import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
+import com.android.internal.infra.AndroidFuture;
/**
* Communication channel from the TrustAgentService back to TrustManagerService.
* @hide
*/
oneway interface ITrustAgentServiceCallback {
- void grantTrust(CharSequence message, long durationMs, int flags);
+ void grantTrust(
+ CharSequence message, long durationMs, int flags, in AndroidFuture resultCallback);
void revokeTrust();
void lockUser();
void setManagingTrust(boolean managingTrust);
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 8f6e1e00c3f4..559313a30dfa 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -19,6 +19,7 @@ package android.service.trust;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
@@ -39,9 +40,12 @@ import android.os.UserManager;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.infra.AndroidFuture;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.function.Consumer;
/**
* A service that notifies the system about whether it believes the environment of the device
@@ -183,6 +187,7 @@ public class TrustAgentService extends Service {
private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
private static final int MSG_USER_REQUESTED_UNLOCK = 10;
+ private static final int MSG_USER_MAY_REQUEST_UNLOCK = 11;
private static final String EXTRA_TOKEN = "token";
private static final String EXTRA_TOKEN_HANDLE = "token_handle";
@@ -217,7 +222,10 @@ public class TrustAgentService extends Service {
onUnlockAttempt(msg.arg1 != 0);
break;
case MSG_USER_REQUESTED_UNLOCK:
- onUserRequestedUnlock();
+ onUserRequestedUnlock(msg.arg1 != 0);
+ break;
+ case MSG_USER_MAY_REQUEST_UNLOCK:
+ onUserMayRequestUnlock();
break;
case MSG_UNLOCK_LOCKOUT:
onDeviceUnlockLockout(msg.arg1);
@@ -297,16 +305,37 @@ public class TrustAgentService extends Service {
}
/**
+ * Called when the user has interacted with the locked device such that they are likely to want
+ * it to be unlocked soon. This approximates the timing when, for example, the platform would
+ * check for face authentication to unlock the device.
+ *
+ * This signal can be used for the agent to make preparations to quickly unlock the device
+ * with {@link #onUserRequestedUnlock}. Agents should not unlock the device based solely on this
+ * signal. There is no guarantee that this method will be called before
+ * {@link #onUserRequestedUnlock(boolean)}.
+ */
+ public void onUserMayRequestUnlock() {
+ }
+
+ /**
* Called when the user has interacted with the locked device such that they likely want it
- * to be unlocked. This approximates the timing when, for example, the platform would check for
- * face authentication to unlock the device.
+ * to be unlocked.
+ *
+ * When this is called, there is a high probability that the user wants to unlock the device and
+ * that a biometric method is either not available or not the optimal method at this time. For
+ * example, this may be called after some kinds of biometric authentication failure.
+ *
+ * A call to this method may be preceded by a call to {@link #onUserMayRequestUnlock} which
+ * the agent can use as a signal to prepare for a subsequent call to this method.
*
* To attempt to unlock the device, the agent needs to call
* {@link #grantTrust(CharSequence, long, int)}.
*
+ * @param dismissKeyguard true when the user wants keyguard dismissed
+ *
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
*/
- public void onUserRequestedUnlock() {
+ public void onUserRequestedUnlock(boolean dismissKeyguard) {
}
/**
@@ -399,26 +428,10 @@ public class TrustAgentService extends Service {
}
/**
- * Call to grant trust on the device.
- *
- * @param message describes why the device is trusted, e.g. "Trusted by location".
- * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
- * Trust for this agent will automatically be revoked when the timeout expires unless
- * extended by a subsequent call to this function. The timeout is measured from the
- * invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
- * For security reasons, the value should be no larger than necessary.
- * The value may be adjusted by the system as necessary to comply with a policy controlled
- * by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
- * for determining when trust expires.
- * @param initiatedByUser this is a hint to the system that trust is being granted as the
- * direct result of user action - such as solving a security challenge. The hint is used
- * by the system to optimize the experience. Behavior may vary by device and release, so
- * one should only set this parameter if it meets the above criteria rather than relying on
- * the behavior of any particular device or release. Corresponds to
- * {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}.
- * @throws IllegalStateException if the agent is not currently managing trust.
+ * Attempts to grant trust on the device.
*
- * @deprecated use {@link #grantTrust(CharSequence, long, int)} instead.
+ * @param initiatedByUser see {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}
+ * @deprecated use {@link #grantTrust(CharSequence, long, int, Consumer)} instead.
*/
@Deprecated
public final void grantTrust(
@@ -427,7 +440,17 @@ public class TrustAgentService extends Service {
}
/**
- * Call to grant trust on the device.
+ * Attempts to grant trust on the device.
+ * @deprecated use {@link #grantTrust(CharSequence, long, int, Consumer)} instead.
+ */
+ @Deprecated
+ public final void grantTrust(
+ final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
+ grantTrust(message, durationMs, flags, null);
+ }
+
+ /**
+ * Attempts to grant trust on the device.
*
* @param message describes why the device is trusted, e.g. "Trusted by location".
* @param durationMs amount of time in milliseconds to keep the device in a trusted state.
@@ -438,19 +461,36 @@ public class TrustAgentService extends Service {
* The value may be adjusted by the system as necessary to comply with a policy controlled
* by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
* for determining when trust expires.
- * @param flags TBDocumented
+ * @param flags flags to control call: see constants prefixed by {@code FLAG_GRANT_TRUST_}.
+ * @param resultCallback may be called with the results of the grant
* @throws IllegalStateException if the agent is not currently managing trust.
+ *
+ * See {@link GrantTrustResult} for the cases where {@code resultCallback} will be called.
*/
public final void grantTrust(
- final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
+ @NonNull final CharSequence message,
+ final long durationMs,
+ @GrantTrustFlags final int flags,
+ @Nullable final Consumer<GrantTrustResult> resultCallback) {
synchronized (mLock) {
if (!mManagingTrust) {
throw new IllegalStateException("Cannot grant trust if agent is not managing trust."
+ " Call setManagingTrust(true) first.");
}
+
+ // Prepare future for the IPC
+ AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
+ future.thenAccept(result -> {
+ if (resultCallback != null) {
+ // Instead of taking an explicit executor, we post this to mHandler to be
+ // consistent with the other event methods in this class.
+ mHandler.post(() -> resultCallback.accept(result));
+ }
+ });
+
if (mCallback != null) {
try {
- mCallback.grantTrust(message.toString(), durationMs, flags);
+ mCallback.grantTrust(message.toString(), durationMs, flags, future);
} catch (RemoteException e) {
onError("calling enableTrust()");
}
@@ -460,7 +500,7 @@ public class TrustAgentService extends Service {
mPendingGrantTrustTask = new Runnable() {
@Override
public void run() {
- grantTrust(message, durationMs, flags);
+ grantTrust(message, durationMs, flags, resultCallback);
}
};
}
@@ -666,8 +706,14 @@ public class TrustAgentService extends Service {
}
@Override
- public void onUserRequestedUnlock() {
- mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK).sendToTarget();
+ public void onUserRequestedUnlock(boolean dismissKeyguard) {
+ mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, dismissKeyguard ? 1 : 0, 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onUserMayRequestUnlock() {
+ mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK).sendToTarget();
}
@Override
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index fee23f4b0be7..9ed57c3aedb1 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -103,7 +103,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* @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
+ * requested width. null if ellipsis is not applied.
* @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
@@ -116,7 +116,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
@NonNull CharSequence source, @NonNull TextPaint paint,
@IntRange(from = 0) int outerWidth,
@NonNull Alignment align, @NonNull BoringLayout.Metrics metrics,
- boolean includePad, @NonNull TextUtils.TruncateAt ellipsize,
+ boolean includePad, @Nullable TextUtils.TruncateAt ellipsize,
@IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) {
return new BoringLayout(source, paint, outerWidth, align, 1f, 0f, metrics, includePad,
ellipsize, ellipsizedWidth, useFallbackLineSpacing);
@@ -146,6 +146,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedWidth = outerwidth;
mEllipsizedStart = 0;
mEllipsizedCount = 0;
+ mUseFallbackLineSpacing = false;
init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
return this;
@@ -169,7 +170,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* @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
+ * requested width. null if ellipsis not applied.
* @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
@@ -181,7 +182,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
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,
+ @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
boolean useFallbackLineSpacing) {
boolean trust;
@@ -200,6 +201,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
trust = false;
}
+ mUseFallbackLineSpacing = useFallbackLineSpacing;
+
init(getText(), paint, align, metrics, includePad, trust,
useFallbackLineSpacing);
return this;
@@ -252,6 +255,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedWidth = outerwidth;
mEllipsizedStart = 0;
mEllipsizedCount = 0;
+ mUseFallbackLineSpacing = false;
init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
}
@@ -294,7 +298,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
* @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}
+ * requested {@code outerWidth}. null if ellipsis is not applied.
* @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
@@ -307,7 +311,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
@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,
+ @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
boolean useFallbackLineSpacing) {
/*
* It is silly to have to call super() and then replaceWith(),
@@ -331,6 +335,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
trust = false;
}
+ mUseFallbackLineSpacing = useFallbackLineSpacing;
init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 43c07c8ab97e..7e0b79470cf2 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -992,6 +992,9 @@ public final class AccessibilityInteractionController {
// Unchecked cast - an app that puts other objects in this bundle with this
// key will crash.
RectF textLocation = ((RectF) textLocations[i]);
+ if (textLocation == null) {
+ continue;
+ }
textLocation.scale(applicationScale);
if (spec != null) {
textLocation.scale(spec.scale);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 406281d4cade..a13579d0acad 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -401,7 +401,7 @@ public class SurfaceControlViewHost {
public void relayout(WindowManager.LayoutParams attrs,
WindowlessWindowManager.ResizeCompleteCallback callback) {
mViewRoot.setLayoutParams(attrs, false);
- mViewRoot.setReportNextDraw(true /* syncBuffer */);
+ mViewRoot.setReportNextDraw();
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 22ccaae9e3c3..413855639d09 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -50,7 +50,6 @@ import android.view.accessibility.IAccessibilityEmbeddedConnection;
import com.android.internal.view.SurfaceCallbackHelper;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -204,6 +203,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private int mSurfaceFlags = SurfaceControl.HIDDEN;
+ private int mPendingReportDraws;
+
/**
* Transaction that should be used from the render thread. This transaction is only thread safe
* with other calls directly from the render thread.
@@ -211,6 +212,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private final SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
/**
+ * Used on the main thread to set the transaction that will be synced with the main window.
+ */
+ private final Transaction mSyncTransaction = new Transaction();
+
+ /**
* Transaction that should be used whe
* {@link HardwareRenderer.FrameDrawingCallback#onFrameDraw} is invoked. All
* frame callbacks can use the same transaction since they will be thread safe
@@ -385,12 +391,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- private void performDrawFinished() {
- mDrawFinished = true;
- if (mAttachedToWindow) {
- mParent.requestTransparentRegion(SurfaceView.this);
- invalidate();
+ private void performDrawFinished(@Nullable Transaction t) {
+ if (t != null) {
+ mSyncTransaction.merge(t);
+ }
+
+ if (mPendingReportDraws > 0) {
+ mDrawFinished = true;
+ if (mAttachedToWindow) {
+ mParent.requestTransparentRegion(SurfaceView.this);
+ notifyDrawFinished();
+ invalidate();
+ }
+ } else {
+ Log.e(TAG, System.identityHashCode(this) + "finished drawing"
+ + " but no pending report draw (extra call"
+ + " to draw completion runnable?)");
+ }
+ }
+
+ void notifyDrawFinished() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.pendingDrawFinished(mSyncTransaction);
}
+ mPendingReportDraws--;
}
@Override
@@ -413,6 +438,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mGlobalListenersAdded = false;
}
+ while (mPendingReportDraws > 0) {
+ notifyDrawFinished();
+ }
+
mRequestedVisible = false;
updateSurface();
@@ -964,17 +993,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- final boolean redrawNeeded = sizeChanged || creating || hintChanged
- || (mVisible && !mDrawFinished);
- final TransactionCallback transactionCallback =
- redrawNeeded ? new TransactionCallback() : null;
- if (redrawNeeded && viewRoot.wasRelayoutRequested()) {
- mBlastBufferQueue.syncNextTransaction(
- false /* acquireSingleBuffer */,
- transactionCallback::onTransactionReady);
- }
final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
+ final boolean redrawNeeded = sizeChanged || creating || hintChanged
+ || (mVisible && !mDrawFinished);
try {
SurfaceHolder.Callback[] callbacks = null;
@@ -993,7 +1015,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mIsCreating = true;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceCreated");
- callbacks = getSurfaceCallbacks();
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
@@ -1011,7 +1035,32 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
if (redrawNeeded) {
- redrawNeeded(callbacks, transactionCallback);
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceRedrawNeeded");
+ if (callbacks == null) {
+ callbacks = getSurfaceCallbacks();
+ }
+
+ final boolean wasRelayoutRequested = viewRoot.wasRelayoutRequested();
+ if (wasRelayoutRequested && (mBlastBufferQueue != null)) {
+ mBlastBufferQueue.syncNextTransaction(
+ false /* acquireSingleBuffer */,
+ this::onDrawFinished);
+ }
+ mPendingReportDraws++;
+ viewRoot.drawPending();
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.stopContinuousSyncTransaction();
+ }
+ // If relayout was requested, then a callback from BBQ will
+ // be invoked with the sync transaction. onDrawFinished will be
+ // called in there
+ if (!wasRelayoutRequested) {
+ onDrawFinished(null);
+ }
+ });
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
}
} finally {
@@ -1030,64 +1079,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- private void redrawNeeded(SurfaceHolder.Callback[] callbacks,
- @Nullable TransactionCallback transactionCallback) {
- if (DEBUG) {
- Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
- }
- final SurfaceHolder.Callback[] capturedCallbacks =
- callbacks == null ? getSurfaceCallbacks() : callbacks;
-
- ViewRootImpl viewRoot = getViewRootImpl();
- boolean isVriSync = viewRoot.addToSync(syncBufferCallback ->
- redrawNeededAsync(capturedCallbacks, () -> {
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.stopContinuousSyncTransaction();
- }
-
- Transaction t = null;
- if (transactionCallback != null && mBlastBufferQueue != null) {
- t = transactionCallback.waitForTransaction();
- }
- // If relayout was requested, then a callback from BBQ will
- // be invoked with the sync transaction. onDrawFinished will be
- // called in there
- syncBufferCallback.onBufferReady(t);
- onDrawFinished();
- }));
-
- // If isVriSync, then everything was setup in the addToSync.
- if (isVriSync) {
- return;
- }
-
- redrawNeededAsync(capturedCallbacks, this::onDrawFinished);
- }
-
- private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
- Runnable callbacksCollected) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected);
- sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
- }
-
- private static class TransactionCallback {
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- private Transaction mTransaction;
-
- Transaction waitForTransaction() {
- try {
- mCountDownLatch.await();
- } catch (InterruptedException e) {
- }
- return mTransaction;
- }
-
- void onTransactionReady(Transaction t) {
- mTransaction = t;
- mCountDownLatch.countDown();
- }
- }
-
/**
* Copy the Surface from the SurfaceControl or the blast adapter.
*
@@ -1198,13 +1189,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
}
- private void onDrawFinished() {
+ private void onDrawFinished(@Nullable Transaction t) {
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " "
+ "finishedDrawing");
}
- runOnUiThread(this::performDrawFinished);
+ runOnUiThread(() -> performDrawFinished(t));
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d15b2a457bfb..172cd03900e7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -23,8 +23,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.SIZE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -401,10 +399,8 @@ public final class ViewRootImpl implements ViewParent,
private static boolean sAlwaysAssignFocus;
/**
- * This list must only be modified by the main thread, so a lock is only needed when changing
- * the list or when accessing the list from a non-main thread.
+ * This list must only be modified by the main thread.
*/
- @GuardedBy("mWindowCallbacks")
final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
@UnsupportedAppUsage
@UiContext
@@ -582,15 +578,11 @@ public final class ViewRootImpl implements ViewParent,
boolean mReportNextDraw;
-
/**
- * Set whether the draw should send the buffer to system server. When set to true, VRI will
- * create a sync transaction with BBQ and send the resulting buffer to system server. If false,
- * VRI will not try to sync a buffer in BBQ, but still report when a draw occurred.
- *
- * Default is true since we normally want to sync the buffer.
+ * Set whether the draw should use blast sync. This is in case the draw is canceled,
+ * but will be rescheduled. We still want the next draw to be sync.
*/
- private boolean mSyncBuffer = true;
+ boolean mNextDrawUseBlastSync;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
@@ -811,10 +803,6 @@ public final class ViewRootImpl implements ViewParent,
return mHandwritingInitiator;
}
- private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
- private int mLastSyncId = -1;
- private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback;
-
/**
* Keeps track of the last frame number that was attempted to draw. Should only be accessed on
* the RenderThread.
@@ -981,15 +969,11 @@ public final class ViewRootImpl implements ViewParent,
}
public void addWindowCallbacks(WindowCallbacks callback) {
- synchronized (mWindowCallbacks) {
- mWindowCallbacks.add(callback);
- }
+ mWindowCallbacks.add(callback);
}
public void removeWindowCallbacks(WindowCallbacks callback) {
- synchronized (mWindowCallbacks) {
- mWindowCallbacks.remove(callback);
- }
+ mWindowCallbacks.remove(callback);
}
public void reportDrawFinish() {
@@ -1711,15 +1695,19 @@ public final class ViewRootImpl implements ViewParent,
final boolean forceNextWindowRelayout = args.argi1 != 0;
final int displayId = args.argi3;
final int resizeMode = args.argi5;
- final Rect backdropFrame = frames.backdropFrame;
- final boolean frameChanged = !mWinFrame.equals(frames.frame);
- final boolean backdropFrameChanged = !mPendingBackDropFrame.equals(backdropFrame);
+ final Rect frame = frames.frame;
+ final Rect displayFrame = frames.displayFrame;
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(frame);
+ mTranslator.translateRectInScreenToAppWindow(displayFrame);
+ }
+ final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean resizeModeChanged = mResizeMode != resizeMode;
- if (msg == MSG_RESIZED && !frameChanged && !backdropFrameChanged && !configChanged
- && !displayChanged && !resizeModeChanged && !forceNextWindowRelayout) {
+ if (msg == MSG_RESIZED && !frameChanged && !configChanged && !displayChanged
+ && !resizeModeChanged && !forceNextWindowRelayout) {
return;
}
@@ -1735,9 +1723,17 @@ public final class ViewRootImpl implements ViewParent,
onMovedToDisplay(displayId, mLastConfigurationFromResources);
}
- setFrame(frames.frame);
- mTmpFrames.displayFrame.set(frames.displayFrame);
- mPendingBackDropFrame.set(backdropFrame);
+ setFrame(frame);
+ mTmpFrames.displayFrame.set(displayFrame);
+
+ if (mDragResizing && mUseMTRenderer) {
+ boolean fullscreen = frame.equals(mPendingBackDropFrame);
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowSizeIsChanging(mPendingBackDropFrame, fullscreen,
+ mAttachInfo.mVisibleInsets, mAttachInfo.mStableInsets);
+ }
+ }
+
mForceNextWindowRelayout = forceNextWindowRelayout;
mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
mSyncSeqId = args.argi4;
@@ -2318,11 +2314,12 @@ public final class ViewRootImpl implements ViewParent,
*/
void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
boolean hasControl) {
- if (type != ITYPE_STATUS_BAR && type != ITYPE_NAVIGATION_BAR) {
+ @InsetsType final int publicType = InsetsState.toPublicType(type);
+ if (publicType != Type.statusBars() && publicType != Type.navigationBars()) {
return;
}
final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo;
- final int systemUiFlag = type == ITYPE_STATUS_BAR
+ final int systemUiFlag = publicType == Type.statusBars()
? View.SYSTEM_UI_FLAG_FULLSCREEN
: View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
final boolean wasVisible = (info.globalVisibility & systemUiFlag) == 0;
@@ -2893,6 +2890,8 @@ public final class ViewRootImpl implements ViewParent,
mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance);
}
}
+ final boolean wasReportNextDraw = mReportNextDraw;
+ boolean useBlastSync = mNextDrawUseBlastSync;
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
@@ -2931,6 +2930,9 @@ public final class ViewRootImpl implements ViewParent,
Log.d(mTag, "Relayout called with blastSync");
}
reportNextDraw();
+ if (isHardwareEnabled()) {
+ useBlastSync = true;
+ }
}
final boolean surfaceControlChanged =
@@ -3166,7 +3168,7 @@ public final class ViewRootImpl implements ViewParent,
// done to achieve a more hermetic fix for S, but it's entirely
// possible that checking the most recent value is actually more
// correct here.
- if (!mStopped || mReportNextDraw) {
+ if (!mStopped || wasReportNextDraw) {
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()
|| dispatchApplyInsets || updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
@@ -3235,7 +3237,7 @@ public final class ViewRootImpl implements ViewParent,
prepareSurfaces();
}
- final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
+ final boolean didLayout = layoutRequested && (!mStopped || wasReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
@@ -3428,40 +3430,51 @@ public final class ViewRootImpl implements ViewParent,
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
- // If we already got a request for blast sync, then we don't want to unset mSyncBuffer
- if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0
- && !mReportNextDraw) {
+ // Remember if we must report the next draw.
+ if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
- mSyncBuffer = false;
}
- boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
- if (!cancelAndRedraw) {
- createSyncIfNeeded();
+ boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
+ if (mBLASTDrawConsumer != null) {
+ useBlastSync = true;
}
- if (!isViewVisible) {
+ if (!cancelDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).endChangingAnimations();
+ mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
-
- if (mSyncBufferCallback != null) {
- mSyncBufferCallback.onBufferReady(null);
- }
- } else if (cancelAndRedraw) {
- // Try again
- scheduleTraversals();
+ performDraw(useBlastSync);
+ mNextDrawUseBlastSync = false;
} else {
- if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
- for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).startChangingAnimations();
+ if (isViewVisible) {
+ // Try again
+ mNextDrawUseBlastSync = useBlastSync;
+ scheduleTraversals();
+ } else {
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).endChangingAnimations();
+ }
+ mPendingTransitions.clear();
+ }
+
+ // We may never draw since it's not visible. Report back that we're finished
+ // drawing.
+ if (!wasReportNextDraw && mReportNextDraw) {
+ mReportNextDraw = false;
+ pendingDrawFinished();
+ }
+
+ // Make sure the consumer is not waiting if the view root was just made invisible.
+ if (mBLASTDrawConsumer != null) {
+ mBLASTDrawConsumer.accept(null);
+ mBLASTDrawConsumer = null;
}
- mPendingTransitions.clear();
}
- performDraw();
}
if (mAttachInfo.mContentCaptureEvents != null) {
@@ -3470,46 +3483,6 @@ public final class ViewRootImpl implements ViewParent,
mIsInTraversal = false;
mRelayoutRequested = false;
-
- if (!cancelAndRedraw) {
- mReportNextDraw = false;
- mSyncBufferCallback = null;
- mSyncBuffer = true;
- if (mLastSyncId != -1) {
- mSurfaceSyncer.markSyncReady(mLastSyncId);
- mLastSyncId = -1;
- }
- }
- }
-
- private void createSyncIfNeeded() {
- // Started a sync already.
- if (mLastSyncId != -1) {
- return;
- }
-
- Consumer<Transaction> syncConsumer = null;
- if (mBLASTDrawConsumer != null) {
- syncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
- } else if (mReportNextDraw) {
- syncConsumer = transaction -> {
- mSurfaceChangedTransaction.merge(transaction);
- reportDrawFinished();
- };
- }
-
- if (syncConsumer != null) {
- final Consumer<Transaction> capturedSyncConsumer = syncConsumer;
- mLastSyncId = mSurfaceSyncer.setupSync(transaction -> {
- // Callback will be invoked on executor thread so post to main thread.
- mHandler.postAtFrontOfQueue(() -> capturedSyncConsumer.accept(transaction));
- });
- if (DEBUG_BLAST) {
- Log.d(mTag, "Setup new sync id=" + mLastSyncId);
- }
- mSurfaceSyncer.addToSync(mLastSyncId, mSyncTarget);
- }
}
private void notifyContentCatpureEvents() {
@@ -4123,10 +4096,54 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ /**
+ * A count of the number of calls to pendingDrawFinished we
+ * require to notify the WM drawing is complete.
+ */
+ int mDrawsNeededToReport = 0;
+
+ /**
+ * Delay notifying WM of draw finished until
+ * a balanced call to pendingDrawFinished.
+ */
+ void drawPending() {
+ mDrawsNeededToReport++;
+ }
+
+ void pendingDrawFinished(Transaction t) {
+ if (mDrawsNeededToReport == 0) {
+ throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls");
+ }
+
+ if (t != null) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Merging transaction into main window transaction");
+ }
+ mSurfaceChangedTransaction.merge(t);
+ }
+
+ mDrawsNeededToReport--;
+ if (mDrawsNeededToReport == 0) {
+ reportDrawFinished();
+ } else if (DEBUG_BLAST) {
+ Log.d(mTag, "pendingDrawFinished. Waiting on draw reported mDrawsNeededToReport="
+ + mDrawsNeededToReport);
+ }
+ }
+
+ void pendingDrawFinished() {
+ pendingDrawFinished(null);
+ }
+
+ private void postDrawFinished() {
+ mHandler.sendEmptyMessage(MSG_DRAW_FINISHED);
+ }
+
private void reportDrawFinished() {
if (DEBUG_BLAST) {
- Log.d(mTag, "reportDrawFinished " + Debug.getCallers(5));
+ Log.d(mTag, "reportDrawFinished");
}
+ mDrawsNeededToReport = 0;
try {
mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction, Integer.MAX_VALUE);
@@ -4145,14 +4162,6 @@ public final class ViewRootImpl implements ViewParent,
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
}
- boolean addToSync(SurfaceSyncer.SyncTarget syncable) {
- if (mLastSyncId == -1) {
- return false;
- }
- mSurfaceSyncer.addToSync(mLastSyncId, syncable);
- return true;
- }
-
private void addFrameCommitCallbackIfNeeded() {
if (!isHardwareEnabled()) {
return;
@@ -4183,81 +4192,188 @@ public final class ViewRootImpl implements ViewParent,
});
}
+ private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync(
+ boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) {
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback "
+ + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+ // draw attempt. The next transaction and transaction complete callback were only set
+ // for the current draw attempt.
+ final Transaction pendingTransactions;
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.syncNextTransaction(null);
+ // Get the transactions that were sent to mergeWithNextTransaction since the
+ // frame didn't draw on this vsync. It's possible the frame will draw later, but
+ // it's better to not be sync than to block on a frame that may never come.
+ pendingTransactions = mBlastBufferQueue.gatherPendingTransactions(
+ mRtLastAttemptedDrawFrameNum);
+ if (!useBlastSync && !reportNextDraw) {
+ pendingTransactions.apply();
+ }
+ } else {
+ pendingTransactions = null;
+ }
+ // Post at front of queue so the buffer can be processed immediately and allow RT
+ // to continue processing new buffers. If RT tries to process buffers before the sync
+ // buffer is applied, the new buffers will not get acquired and could result in a
+ // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free
+ // buffer.
+ mHandler.postAtFrontOfQueue(() -> {
+ if (!didProduceBuffer && useBlastSync) {
+ mSurfaceChangedTransaction.merge(pendingTransactions);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
+ }
+
+ // This is to ensure pendingDrawFinished is only called exactly one time per draw
+ // attempt when reportNextDraw is true. Since, we sometimes create a sync
+ // transaction callback, the callback will handle calling pendingDrawFinished.
+ // However, there are cases where the transaction callback may not be called.
+ // 1. If useBlastSync is false, then we know that a sync transaction callback was
+ // not created so we won't invoke pendingDrawFinished there.
+ // 2. If the draw didn't produce a frame, didProduceBuffer == false, then we know
+ // the sync transaction callback will not be invoked even if one was set up.
+ if (reportNextDraw && (!didProduceBuffer || !useBlastSync)) {
+ pendingDrawFinished();
+ }
+ });
+
+ };
+ }
+
@Nullable
- private void registerFrameDrawingCallbackForBlur() {
+ private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync,
+ boolean reportNextDraw) {
if (!isHardwareEnabled()) {
- return;
+ return null;
}
final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
- if (!needsCallbackForBlur) {
- return;
+ if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw && !mHasPendingTransactions) {
+ return null;
+ }
+
+ final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Creating frameDrawingCallback"
+ + " nextDrawUseBlastSync=" + useBlastSync
+ + " reportNextDraw=" + reportNextDraw
+ + " hasBlurUpdates=" + hasBlurUpdates
+ + " hasBlastSyncConsumer=" + (blastSyncConsumer != null)
+ + " mHasPendingTransactions=" + mHasPendingTransactions);
}
final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
- mBlurRegionAggregator.getBlurRegionsCopyForRT();
+ needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
+ final boolean hasPendingTransactions = mHasPendingTransactions;
+ mHasPendingTransactions = false;
+
// The callback will run on the render thread.
- registerRtFrameCallback((frame) -> mBlurRegionAggregator
- .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates));
- }
+ return new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
- private void registerCallbackForPendingTransactions() {
- registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
+
+ mRtLastAttemptedDrawFrameNum = frame;
+
+ if (needsCallbackForBlur) {
+ mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame,
+ blurRegionsForFrame, hasBlurUpdates);
+ }
+
+ if (mBlastBufferQueue == null) {
+ return null;
+ }
+
+ if (!useBlastSync && !reportNextDraw && !hasPendingTransactions) {
+ return null;
+ }
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
if ((syncResult
& (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
- mBlastBufferQueue.applyPendingTransactions(frame);
+ if (reportNextDraw) {
+ mHandler.postAtFrontOfQueue(() -> pendingDrawFinished());
+ }
return null;
}
- return didProduceBuffer -> {
- if (!didProduceBuffer) {
- mBlastBufferQueue.applyPendingTransactions(frame);
- }
- };
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
- }
+ if (useBlastSync) {
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame.
+ mBlastBufferQueue.syncNextTransaction(
+ t -> {
+ mHandler.postAtFrontOfQueue(() -> {
+ mSurfaceChangedTransaction.merge(t);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
- @Override
- public void onFrameDraw(long frame) {
+ if (reportNextDraw) {
+ pendingDrawFinished();
+ }
+ });
+ });
+ }
+
+ return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw,
+ blastSyncConsumer);
}
- });
+ };
}
- private void performDraw() {
+ private void performDraw(boolean useBlastSync) {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || mSyncBufferCallback != null;
+ final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw || useBlastSync;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- registerFrameDrawingCallbackForBlur();
- addFrameCommitCallbackIfNeeded();
-
- boolean usingAsyncReport = isHardwareEnabled() && mSyncBufferCallback != null;
- if (usingAsyncReport) {
- registerCallbacksForSync(mSyncBuffer, mSyncBufferCallback);
- } else if (mHasPendingTransactions) {
- // These callbacks are only needed if there's no sync involved and there were calls to
- // applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
- // apply those transactions directly so they don't get stuck forever.
- registerCallbackForPendingTransactions();
+ FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync,
+ mReportNextDraw);
+ if (frameDrawingCallback != null) {
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback);
}
- mHasPendingTransactions = false;
+ addFrameCommitCallbackIfNeeded();
+ boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw);
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCallback(null);
usingAsyncReport = false;
+ mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback);
}
} finally {
mIsDrawing = false;
@@ -4275,6 +4391,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mReportNextDraw) {
+ mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
@@ -4295,11 +4412,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- final SurfaceSyncer.SyncBufferCallback syncBufferCallback = mSyncBufferCallback;
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
- mHandler.post(() -> syncBufferCallback.onBufferReady(null)));
- mSyncBufferCallback = null;
-
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
@@ -4307,11 +4420,9 @@ public final class ViewRootImpl implements ViewParent,
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
}
+ pendingDrawFinished();
}
}
- if (mSyncBufferCallback != null && !usingAsyncReport) {
- mSyncBufferCallback.onBufferReady(null);
- }
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
@@ -5320,6 +5431,7 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
private static final int MSG_UPDATE_POINTER_ICON = 27;
private static final int MSG_POINTER_CAPTURE_CHANGED = 28;
+ private static final int MSG_DRAW_FINISHED = 29;
private static final int MSG_INSETS_CHANGED = 30;
private static final int MSG_INSETS_CONTROL_CHANGED = 31;
private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
@@ -5382,6 +5494,8 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_UPDATE_POINTER_ICON";
case MSG_POINTER_CAPTURE_CHANGED:
return "MSG_POINTER_CAPTURE_CHANGED";
+ case MSG_DRAW_FINISHED:
+ return "MSG_DRAW_FINISHED";
case MSG_INSETS_CHANGED:
return "MSG_INSETS_CHANGED";
case MSG_INSETS_CONTROL_CHANGED:
@@ -5510,8 +5624,6 @@ public final class ViewRootImpl implements ViewParent,
mTmpFrames.frame.top = t;
mTmpFrames.frame.bottom = t + h;
setFrame(mTmpFrames.frame);
-
- mPendingBackDropFrame.set(mWinFrame);
maybeHandleWindowMove(mWinFrame);
}
break;
@@ -5614,6 +5726,9 @@ public final class ViewRootImpl implements ViewParent,
final boolean hasCapture = msg.arg1 != 0;
handlePointerCaptureChanged(hasCapture);
} break;
+ case MSG_DRAW_FINISHED: {
+ pendingDrawFinished();
+ } break;
case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
systemGestureExclusionChanged();
} break;
@@ -8006,7 +8121,6 @@ public final class ViewRootImpl implements ViewParent,
getConfiguration().windowConfiguration.getBounds());
}
- mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
@@ -8037,6 +8151,7 @@ public final class ViewRootImpl implements ViewParent,
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
}
@@ -8081,6 +8196,13 @@ public final class ViewRootImpl implements ViewParent,
private void setFrame(Rect frame) {
mWinFrame.set(frame);
+
+ // Surface position is now inherited from parent, and BackdropFrameRenderer uses backdrop
+ // frame to position content. Thus, we just keep the size of backdrop frame, and remove the
+ // offset to avoid double offset from display origin.
+ mPendingBackDropFrame.set(frame);
+ mPendingBackDropFrame.offsetTo(0, 0);
+
mInsetsController.onFrameChanged(mOverrideInsetsFrame != null ?
mOverrideInsetsFrame : frame);
}
@@ -8472,28 +8594,7 @@ public final class ViewRootImpl implements ViewParent,
private void dispatchResized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, boolean forceLayout,
boolean alwaysConsumeSystemBars, int displayId, int seqId, int resizeMode) {
- final Rect frame = frames.frame;
- final Rect backDropFrame = frames.backdropFrame;
- if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
- + " reportDraw=" + reportDraw
- + " backDropFrame=" + backDropFrame);
-
- // Tell all listeners that we are resizing the window so that the chrome can get
- // updated as fast as possible on a separate thread,
- if (mDragResizing && mUseMTRenderer) {
- boolean fullscreen = frame.equals(backDropFrame);
- synchronized (mWindowCallbacks) {
- for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
- mAttachInfo.mVisibleInsets, mAttachInfo.mStableInsets);
- }
- }
- }
-
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(frame);
- }
SomeArgs args = SomeArgs.obtain();
final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
args.arg1 = sameProcessCall ? new ClientWindowFrames(frames) : frames;
@@ -9795,8 +9896,8 @@ public final class ViewRootImpl implements ViewParent,
}
private void reportNextDraw() {
- if (DEBUG_BLAST) {
- Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
+ if (mReportNextDraw == false) {
+ drawPending();
}
mReportNextDraw = true;
}
@@ -9807,14 +9908,9 @@ public final class ViewRootImpl implements ViewParent,
* This method is only supposed to be used to speed up the interaction from SystemUI and window
* manager when waiting for the first frame to be drawn when turning on the screen. DO NOT USE
* unless you fully understand this interaction.
- *
- * @param syncBuffer If true, the transaction that contains the buffer from the draw should be
- * sent to system to be synced. If false, VRI will not try to sync the buffer,
- * but only report back that a buffer was drawn.
* @hide
*/
- public void setReportNextDraw(boolean syncBuffer) {
- mSyncBuffer = syncBuffer;
+ public void setReportNextDraw() {
reportNextDraw();
invalidate();
}
@@ -10840,15 +10936,14 @@ public final class ViewRootImpl implements ViewParent,
return mWindowSession;
}
- private void registerCallbacksForSync(boolean syncBuffer,
+ private void registerCallbacksForSync(
final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
if (!isHardwareEnabled()) {
+ // TODO: correctly handle when hardware disabled
+ syncBufferCallback.onBufferReady(null);
return;
}
- if (DEBUG_BLAST) {
- Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
- }
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -10877,9 +10972,7 @@ public final class ViewRootImpl implements ViewParent,
Log.d(mTag, "Setting up sync and frameCommitCallback");
}
- if (syncBuffer) {
- mBlastBufferQueue.syncNextTransaction(syncBufferCallback::onBufferReady);
- }
+ mBlastBufferQueue.syncNextTransaction(t -> syncBufferCallback.onBufferReady(t));
return didProduceBuffer -> {
if (DEBUG_BLAST) {
@@ -10893,40 +10986,18 @@ public final class ViewRootImpl implements ViewParent,
// were only set for the current draw attempt.
if (!didProduceBuffer) {
mBlastBufferQueue.syncNextTransaction(null);
-
// Gather the transactions that were sent to mergeWithNextTransaction
// since the frame didn't draw on this vsync. It's possible the frame will
// draw later, but it's better to not be sync than to block on a frame that
// may never come.
syncBufferCallback.onBufferReady(
mBlastBufferQueue.gatherPendingTransactions(frame));
- return;
- }
-
- // If we didn't request to sync a buffer, then we won't get the
- // syncNextTransaction callback. Instead, just report back to the Syncer so it
- // knows that this sync request is complete.
- if (!syncBuffer) {
- syncBufferCallback.onBufferReady(null);
}
};
}
});
}
- public final SurfaceSyncer.SyncTarget mSyncTarget = this::readyToSync;
-
- private void readyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
- if (mSyncBufferCallback != null) {
- Log.d(mTag, "Already set sync for the next draw.");
- mSyncBufferCallback.onBufferReady(null);
- }
- if (DEBUG_BLAST) {
- Log.d(mTag, "Setting syncFrameCallback");
- }
- mSyncBufferCallback = syncBufferCallback;
- if (!mIsInTraversal && !mTraversalScheduled) {
- scheduleTraversals();
- }
- }
+ public final SurfaceSyncer.SyncTarget mSyncTarget =
+ syncBufferCallback -> registerCallbacksForSync(syncBufferCallback);
}
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index a3b1313202c2..b7b71f123dad 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -179,6 +179,8 @@ public class WindowLayout {
final boolean hasCompatScale = compatScale != 1f;
final int pw = outParentFrame.width();
final int ph = outParentFrame.height();
+ final boolean extendedByCutout =
+ (attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
int rw = requestedWidth;
int rh = requestedHeight;
float x, y;
@@ -186,11 +188,13 @@ public class WindowLayout {
// If the view hierarchy hasn't been measured, the requested width and height would be
// UNSPECIFIED_LENGTH. This can happen in the first layout of a window or in the simulated
- // layout.
- if (rw == UNSPECIFIED_LENGTH) {
+ // layout. If extendedByCutout is true, we cannot use the requested lengths. Otherwise,
+ // the window frame might be extended again because the requested lengths may come from the
+ // window frame.
+ if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
rw = attrs.width >= 0 ? attrs.width : pw;
}
- if (rh == UNSPECIFIED_LENGTH) {
+ if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
rh = attrs.height >= 0 ? attrs.height : ph;
}
@@ -262,29 +266,15 @@ public class WindowLayout {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
- if ((attrs.privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0
- && !cutout.isEmpty()) {
- // If the actual frame covering a display cutout, and the window is requesting to extend
- // it's requested frame, re-do the frame calculation after getting the new requested
- // size.
+ if (extendedByCutout && !displayCutoutSafe.contains(outFrame)) {
mTempRect.set(outFrame);
- // Do nothing if the display cutout and window don't overlap entirely. This may happen
- // when the cutout is not on the same side with the window.
- boolean shouldExpand = false;
- final Rect [] cutoutBounds = cutout.getBoundingRectsAll();
- for (Rect cutoutBound : cutoutBounds) {
- if (cutoutBound.isEmpty()) continue;
- if (mTempRect.contains(cutoutBound) || cutoutBound.contains(mTempRect)) {
- shouldExpand = true;
- break;
- }
- }
- if (shouldExpand) {
- // Try to fit move the bar to avoid the display cutout first. Make sure the clip
- // flags are not set to make the window move.
- final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL;
- Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe,
- mTempRect);
+
+ // Move the frame into displayCutoutSafe.
+ final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL;
+ Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe,
+ mTempRect);
+
+ if (mTempRect.intersect(outDisplayFrame)) {
outFrame.union(mTempRect);
}
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index fadbdbbe8746..464414d861fd 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -646,11 +646,9 @@ public abstract class Animation implements Cloneable {
* @param bg The background color. If 0, no background. Currently must
* be black, with any desired alpha level.
*
- * @deprecated None of window animations are running with background color.
*/
- @Deprecated
public void setBackgroundColor(@ColorInt int bg) {
- // The background color is not needed any more, do nothing.
+ mBackgroundColor = bg;
}
/**
@@ -824,13 +822,10 @@ public abstract class Animation implements Cloneable {
/**
* Returns the background color behind the animation.
- *
- * @deprecated None of window animations are running with background color.
*/
- @Deprecated
@ColorInt
public int getBackgroundColor() {
- return 0;
+ return mBackgroundColor;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index fd336a27bb67..6209b46997e8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -414,4 +414,12 @@ public interface InputMethod {
// intentionally empty
}
+ /**
+ * Finish stylus handwriting session.
+ * @hide
+ */
+ default void finishStylusHandwriting() {
+ // intentionally empty
+ }
+
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7f8a68dbd0ea..67f284b437b0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6309,7 +6309,8 @@ public class Editor {
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)
+ || (mTextView.isAutoHandwritingEnabled() && isFromStylus(event))) {
break;
}
if (mIsDraggingCursor) {
@@ -6332,6 +6333,11 @@ public class Editor {
}
}
+ private boolean isFromStylus(MotionEvent motionEvent) {
+ final int pointerIndex = motionEvent.getActionIndex();
+ return motionEvent.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_STYLUS;
+ }
+
private void positionCursorDuringDrag(MotionEvent event) {
mPrevLineDuringDrag = getLineDuringDrag(event);
int offset = mTextView.getOffsetAtCoordinate(mPrevLineDuringDrag, event.getX());
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 5b915cc22ec5..51f3fe2551ad 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -40,9 +40,6 @@ public class ClientWindowFrames implements Parcelable {
*/
public final @NonNull Rect parentFrame = new Rect();
- /** The background area while the window is resizing. */
- public final @NonNull Rect backdropFrame = new Rect();
-
public boolean isParentFrameClippedByDisplayCutout;
public ClientWindowFrames() {
@@ -52,7 +49,6 @@ public class ClientWindowFrames implements Parcelable {
frame.set(other.frame);
displayFrame.set(other.displayFrame);
parentFrame.set(other.parentFrame);
- backdropFrame.set(other.backdropFrame);
isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
}
@@ -65,7 +61,6 @@ public class ClientWindowFrames implements Parcelable {
frame.readFromParcel(in);
displayFrame.readFromParcel(in);
parentFrame.readFromParcel(in);
- backdropFrame.readFromParcel(in);
isParentFrameClippedByDisplayCutout = in.readBoolean();
}
@@ -74,7 +69,6 @@ public class ClientWindowFrames implements Parcelable {
frame.writeToParcel(dest, flags);
displayFrame.writeToParcel(dest, flags);
parentFrame.writeToParcel(dest, flags);
- backdropFrame.writeToParcel(dest, flags);
dest.writeBoolean(isParentFrameClippedByDisplayCutout);
}
@@ -84,7 +78,6 @@ public class ClientWindowFrames implements Parcelable {
return "ClientWindowFrames{frame=" + frame.toShortString(sb)
+ " display=" + displayFrame.toShortString(sb)
+ " parentFrame=" + parentFrame.toShortString(sb)
- + " backdrop=" + backdropFrame.toShortString(sb)
+ " parentClippedByDisplayCutout=" + isParentFrameClippedByDisplayCutout + "}";
}
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index fab180dae0bf..f1c0d8dee525 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -100,8 +100,12 @@ public interface SplashScreen {
* <p>
* To reset to the default theme, set this the themeId to {@link Resources#ID_NULL}.
* <p>
- * <b>Note:</b> The theme name must be stable across versions, otherwise it won't be found
- * after your application is updated.
+ * <b>Note:</b> Internally, the theme name is resolved and persisted. This means that the theme
+ * name must be stable across versions, otherwise it won't be found after your application is
+ * updated.
+ *
+ * @param themeId The ID of the splashscreen theme to be used in place of the one defined in
+ * the manifest.
*/
void setSplashScreenTheme(@StyleRes int themeId);
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 10d7ecab4ffa..19ee1d046184 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -233,14 +233,6 @@ public final class SplashScreenView extends FrameLayout {
}
/**
- * Set the animation duration if icon is animatable.
- */
- public Builder setAnimationDurationMillis(long duration) {
- mIconAnimationDuration = Duration.ofMillis(duration);
- return this;
- }
-
- /**
* Set the Runnable that can receive the task which should be executed on UI thread.
* @param uiThreadInitTask
*/
@@ -294,8 +286,7 @@ public final class SplashScreenView extends FrameLayout {
} else {
view.mIconView = createSurfaceView(view);
}
- view.initIconAnimation(mIconDrawable,
- mIconAnimationDuration != null ? mIconAnimationDuration.toMillis() : 0);
+ view.initIconAnimation(mIconDrawable);
view.mIconAnimationStart = mIconAnimationStart;
view.mIconAnimationDuration = mIconAnimationDuration;
} else if (mIconSize != 0) {
@@ -463,6 +454,11 @@ public final class SplashScreenView extends FrameLayout {
/**
* Returns the duration of the icon animation if icon is animatable.
*
+ * Note the return value can be null or 0 if the
+ * {@link android.R.attr#windowSplashScreenAnimatedIcon} is not
+ * {@link android.graphics.drawable.AnimationDrawable} or
+ * {@link android.graphics.drawable.AnimatedVectorDrawable}.
+ *
* @see android.R.attr#windowSplashScreenAnimatedIcon
* @see android.R.attr#windowSplashScreenAnimationDuration
*/
@@ -497,12 +493,12 @@ public final class SplashScreenView extends FrameLayout {
mSurfaceView.setChildSurfacePackage(mSurfacePackage);
}
- void initIconAnimation(Drawable iconDrawable, long duration) {
+ void initIconAnimation(Drawable iconDrawable) {
if (!(iconDrawable instanceof IconAnimateListener)) {
return;
}
IconAnimateListener aniDrawable = (IconAnimateListener) iconDrawable;
- aniDrawable.prepareAnimate(duration, this::animationStartCallback);
+ aniDrawable.prepareAnimate(this::animationStartCallback);
aniDrawable.setAnimationJankMonitoring(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
@@ -524,7 +520,7 @@ public final class SplashScreenView extends FrameLayout {
private void animationStartCallback(long animDuration) {
mIconAnimationStart = Instant.now();
- if (animDuration > 0) {
+ if (animDuration >= 0) {
mIconAnimationDuration = Duration.ofMillis(animDuration);
}
}
@@ -695,10 +691,9 @@ public final class SplashScreenView extends FrameLayout {
public interface IconAnimateListener {
/**
* Prepare the animation if this drawable also be animatable.
- * @param duration The animation duration.
* @param startListener The callback listener used to receive the start of the animation.
*/
- void prepareAnimate(long duration, LongConsumer startListener);
+ void prepareAnimate(LongConsumer startListener);
/**
* Stop animation.
diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java
new file mode 100644
index 000000000000..76e58988eedf
--- /dev/null
+++ b/core/java/com/android/internal/app/AppLocaleStore.java
@@ -0,0 +1,85 @@
+/*
+ * 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.internal.app;
+
+import android.app.LocaleConfig;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.LocaleList;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public class AppLocaleStore {
+ private static final String TAG = AppLocaleStore.class.getSimpleName();
+
+ public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) {
+ ArrayList<Locale> appSupportedLocales = new ArrayList<>();
+ LocaleList packageLocaleList = getPackageLocales(context, packageName);
+
+ if (packageLocaleList != null && packageLocaleList.size() > 0) {
+ for (int i = 0; i < packageLocaleList.size(); i++) {
+ appSupportedLocales.add(packageLocaleList.get(i));
+ }
+ Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: "
+ + appSupportedLocales.size());
+ } else {
+ String[] languages = getAssetLocales(context, packageName);
+ for (String language : languages) {
+ appSupportedLocales.add(Locale.forLanguageTag(language));
+ }
+ Log.d(TAG, "getAppSupportedLocales from asset. Size: "
+ + appSupportedLocales.size());
+ }
+ return appSupportedLocales;
+ }
+
+ private static LocaleList getPackageLocales(Context context, String packageName) {
+ try {
+ LocaleConfig localeConfig =
+ new LocaleConfig(context.createPackageContext(packageName, 0));
+ if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) {
+ return localeConfig.getSupportedLocales();
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
+ }
+ return null;
+ }
+
+ private static String[] getAssetLocales(Context context, String packageName) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ String[] locales = packageManager.getResourcesForApplication(
+ packageManager.getPackageInfo(packageName, PackageManager.MATCH_ALL)
+ .applicationInfo).getAssets().getNonSystemLocales();
+ if (locales == null) {
+ Log.i(TAG, "[" + packageName + "] locales are null.");
+ return new String[0];
+ } else if (locales.length <= 0) {
+ Log.i(TAG, "[" + packageName + "] locales length is 0.");
+ return new String[0];
+ }
+ return locales;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Can not found the package name : " + packageName + " / " + e);
+ }
+ return new String[0];
+ }
+
+}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 70506ccaebb8..c2e145eccb97 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -107,7 +107,6 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.Space;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -1054,7 +1053,6 @@ public class ChooserActivity extends ResolverActivity implements
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClipAsPackage(clipData, getReferrerPackageName());
- Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
// Log share completion via copy
LogMaker targetLogMaker = new LogMaker(
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index 707286e0fc4c..57bd3f945358 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -234,7 +234,11 @@ public class LocaleHelper {
public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) {
// We don't care about the various suggestion types, just "suggested" (!= 0)
// and "all others" (== 0)
- if (lhs.isSuggested() == rhs.isSuggested()) {
+ if (lhs.isAppCurrentLocale() || rhs.isAppCurrentLocale()) {
+ return lhs.isAppCurrentLocale() ? -1 : 1;
+ } else if (lhs.isSystemLocale() || rhs.isSystemLocale()) {
+ return lhs.isSystemLocale() ? -1 : 1;
+ } else if (lhs.isSuggested() == rhs.isSuggested()) {
// They are in the same "bucket" (suggested / others), so we compare the text
return mCollator.compare(
removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)),
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index b4ae56f23443..e7cb43e019ad 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.TextUtils;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -32,6 +33,7 @@ import android.widget.SearchView;
import com.android.internal.R;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
@@ -45,6 +47,7 @@ import java.util.Set;
* default locale.</p>
*/
public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener {
+ private static final String TAG = LocalePickerWithRegion.class.getSimpleName();
private static final String PARENT_FRAGMENT_NAME = "localeListEditor";
private SuggestedLocaleAdapter mAdapter;
@@ -57,6 +60,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
private boolean mPreviousSearchHadFocus = false;
private int mFirstVisiblePosition = 0;
private int mTopDistance = 0;
+ private String mAppPackageName;
/**
* Other classes can register to be notified when a locale was selected.
@@ -73,17 +77,25 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
private static LocalePickerWithRegion createCountryPicker(Context context,
LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
- boolean translatedOnly) {
+ boolean translatedOnly, String appPackageName) {
LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
boolean shouldShowTheList = localePicker.setListener(context, listener, parent,
- translatedOnly);
+ translatedOnly, appPackageName);
return shouldShowTheList ? localePicker : null;
}
public static LocalePickerWithRegion createLanguagePicker(Context context,
LocaleSelectedListener listener, boolean translatedOnly) {
LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
- localePicker.setListener(context, listener, /* parent */ null, translatedOnly);
+ localePicker.setListener(context, listener, /* parent */ null, translatedOnly, null);
+ return localePicker;
+ }
+
+ public static LocalePickerWithRegion createLanguagePicker(Context context,
+ LocaleSelectedListener listener, boolean translatedOnly, String appPackageName) {
+ LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
+ localePicker.setListener(
+ context, listener, /* parent */ null, translatedOnly, appPackageName);
return localePicker;
}
@@ -101,20 +113,32 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
* "pretending" it was selected, and return false.</p>
*/
private boolean setListener(Context context, LocaleSelectedListener listener,
- LocaleStore.LocaleInfo parent, boolean translatedOnly) {
+ LocaleStore.LocaleInfo parent, boolean translatedOnly, String appPackageName) {
this.mParentLocale = parent;
this.mListener = listener;
this.mTranslatedOnly = translatedOnly;
+ this.mAppPackageName = appPackageName;
setRetainInstance(true);
final HashSet<String> langTagsToIgnore = new HashSet<>();
- if (!translatedOnly) {
+ LocaleStore.LocaleInfo appCurrentLocale =
+ LocaleStore.getAppCurrentLocaleInfo(context, appPackageName);
+ boolean isForCountryMode = parent != null;
+
+ if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) {
+ if (appCurrentLocale != null) {
+ Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag());
+ langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag());
+ } else {
+ Log.d(TAG, "appCurrentLocale is null");
+ }
+ } else if (!translatedOnly) {
final LocaleList userLocales = LocalePicker.getLocales();
final String[] langTags = userLocales.toLanguageTags().split(",");
Collections.addAll(langTagsToIgnore, langTags);
}
- if (parent != null) {
+ if (isForCountryMode) {
mLocaleList = LocaleStore.getLevelLocales(context,
langTagsToIgnore, parent, translatedOnly);
if (mLocaleList.size() <= 1) {
@@ -127,10 +151,39 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore,
null /* no parent */, translatedOnly);
}
+ Log.d(TAG, "mLocaleList size: " + mLocaleList.size());
+
+ // Adding current locale and system default option into suggestion list
+ if(!TextUtils.isEmpty(appPackageName)) {
+ if (appCurrentLocale != null && !isForCountryMode) {
+ mLocaleList.add(appCurrentLocale);
+ }
+ filterTheLanguagesNotSupportedInApp(context, appPackageName);
+ if (!isForCountryMode) {
+ mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo());
+ }
+ }
return true;
}
+ private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) {
+ ArrayList<Locale> supportedLocales =
+ AppLocaleStore.getAppSupportedLocales(context, appPackageName);
+
+ Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
+ for(LocaleStore.LocaleInfo li: mLocaleList) {
+ for(Locale l: supportedLocales) {
+ if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
+ filteredList.add(li);
+ }
+ }
+ }
+ Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size());
+
+ mLocaleList = filteredList;
+ }
+
private void returnToParentFrame() {
getFragmentManager().popBackStack(PARENT_FRAGMENT_NAME,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
@@ -151,7 +204,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
final boolean countryMode = mParentLocale != null;
final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
- mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode);
+ mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, mAppPackageName);
final LocaleHelper.LocaleInfoComparator comp =
new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
mAdapter.sort(comp);
@@ -212,17 +265,22 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
+ SuggestedLocaleAdapter adapter = (SuggestedLocaleAdapter) getListAdapter();
final LocaleStore.LocaleInfo locale =
- (LocaleStore.LocaleInfo) getListAdapter().getItem(position);
+ (LocaleStore.LocaleInfo) adapter.getItem(position);
+ // Special case for resetting the app locale to equal the system locale.
+ boolean isSystemLocale = locale.isSystemLocale();
+ boolean isRegionLocale = locale.getParent() != null;
- if (locale.getParent() != null) {
+ if (isSystemLocale || isRegionLocale) {
if (mListener != null) {
mListener.onLocaleSelected(locale);
}
returnToParentFrame();
} else {
LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
- getContext(), mListener, locale, mTranslatedOnly /* translate only */);
+ getContext(), mListener, locale, mTranslatedOnly /* translate only */,
+ adapter.getAppPackageName());
if (selector != null) {
getFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 1c5ca593ebba..cea8eaa3ee7f 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -16,11 +16,13 @@
package com.android.internal.app;
+import android.app.LocaleManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.LocaleList;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.Log;
import java.io.Serializable;
import java.util.HashMap;
@@ -31,12 +33,17 @@ import java.util.Set;
public class LocaleStore {
private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>();
+ private static final String TAG = LocaleStore.class.getSimpleName();
private static boolean sFullyInitialized = false;
public static class LocaleInfo implements Serializable {
private static final int SUGGESTION_TYPE_NONE = 0;
private static final int SUGGESTION_TYPE_SIM = 1 << 0;
private static final int SUGGESTION_TYPE_CFG = 1 << 1;
+ // Only for per-app language picker
+ private static final int SUGGESTION_TYPE_CURRENT = 1 << 2;
+ // Only for per-app language picker
+ private static final int SUGGESTION_TYPE_SYSTEM_LANGUAGE = 1 << 3;
private final Locale mLocale;
private final Locale mParent;
@@ -189,6 +196,14 @@ public class LocaleStore {
public void setChecked(boolean checked) {
mIsChecked = checked;
}
+
+ public boolean isAppCurrentLocale() {
+ return (mSuggestionFlags & SUGGESTION_TYPE_CURRENT) > 0;
+ }
+
+ public boolean isSystemLocale() {
+ return (mSuggestionFlags & SUGGESTION_TYPE_SYSTEM_LANGUAGE) > 0;
+ }
}
private static Set<String> getSimCountries(Context context) {
@@ -239,6 +254,40 @@ public class LocaleStore {
}
}
+ public static LocaleInfo getAppCurrentLocaleInfo(Context context, String appPackageName) {
+ if (appPackageName == null) {
+ return null;
+ }
+
+ LocaleManager localeManager = context.getSystemService(LocaleManager.class);
+ try {
+ LocaleList localeList = (localeManager == null)
+ ? null : localeManager.getApplicationLocales(appPackageName);
+ Locale locale = localeList == null ? null : localeList.get(0);
+
+ if (locale != null) {
+ LocaleInfo localeInfo = new LocaleInfo(locale);
+ localeInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT;
+ localeInfo.mIsTranslated = true;
+ return localeInfo;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "IllegalArgumentException ", e);
+ }
+ return null;
+ }
+
+ /**
+ * The "system default" is special case for per-app picker. Intentionally keep the locale
+ * empty to let activity know "system default" been selected.
+ */
+ public static LocaleInfo getSystemDefaultLocaleInfo() {
+ LocaleInfo systemDefaultInfo = new LocaleInfo("");
+ systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE;
+ systemDefaultInfo.mIsTranslated = true;
+ return systemDefaultInfo;
+ }
+
/*
* Show all the languages supported for a country in the suggested list.
* This is also handy for devices without SIM (tablets).
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index e6c911e5b41d..3833976fcdb1 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -9,3 +9,6 @@ per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
per-file *Assist* = file:/core/java/android/service/voice/OWNERS
per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
per-file *Voice* = file:/core/java/android/service/voice/OWNERS
+
+# System language settings
+per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS \ No newline at end of file
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 46f47a31441c..c3e7920ffc08 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -53,6 +53,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
private static final int TYPE_HEADER_SUGGESTED = 0;
private static final int TYPE_HEADER_ALL_OTHERS = 1;
private static final int TYPE_LOCALE = 2;
+ private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3;
private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6;
private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions;
@@ -64,10 +65,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
private Locale mDisplayLocale = null;
// used to potentially cache a modified Context that uses mDisplayLocale
private Context mContextOverride = null;
+ private String mAppPackageName;
public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) {
+ this(localeOptions, countryMode, null);
+ }
+
+ public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode,
+ String appPackageName) {
mCountryMode = countryMode;
mLocaleOptions = new ArrayList<>(localeOptions.size());
+ mAppPackageName = appPackageName;
+
for (LocaleStore.LocaleInfo li : localeOptions) {
if (li.isSuggested()) {
mSuggestionCount++;
@@ -83,7 +92,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
@Override
public boolean isEnabled(int position) {
- return getItemViewType(position) == TYPE_LOCALE;
+ return getItemViewType(position) == TYPE_LOCALE
+ || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER;
}
@Override
@@ -97,13 +107,20 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
if (position == mSuggestionCount + 1) {
return TYPE_HEADER_ALL_OTHERS;
}
+
+ LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position);
+ if (item.isSystemLocale()) {
+ return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER;
+ }
return TYPE_LOCALE;
}
}
@Override
public int getViewTypeCount() {
- if (showHeaders()) {
+ if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) {
+ return 4; // Two headers and 1 for "System language"
+ } else if (showHeaders()) {
return 3; // Two headers in addition to the locales
} else {
return 1; // Locales items only
@@ -187,6 +204,21 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
textView.setTextLocale(
mDisplayLocale != null ? mDisplayLocale : Locale.getDefault());
break;
+
+ case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
+ if (!(convertView instanceof ViewGroup)) {
+ convertView = mInflater.inflate(
+ R.layout.app_language_picker_system_default, parent, false);
+ }
+
+ Locale defaultLocale = Locale.getDefault();
+ TextView title = convertView.findViewById(R.id.locale);
+ title.setText(R.string.system_locale_title);
+ title.setTextLocale(defaultLocale);
+ TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle);
+ subtitle.setText(defaultLocale.getDisplayName());
+ subtitle.setTextLocale(defaultLocale);
+ break;
default:
// Covers both null, and "reusing" a wrong kind of view
if (!(convertView instanceof ViewGroup)) {
@@ -316,4 +348,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
public Filter getFilter() {
return new FilterByNativeAndUiNames();
}
+
+ public String getAppPackageName() {
+ return mAppPackageName;
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index b18c98b58516..2ee47b64b1a5 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -43,5 +43,5 @@ oneway interface IInputMethodPrivilegedOperations {
void notifyUserActionAsync();
void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
void onStylusHandwritingReady(int requestId, int pid);
- void finishStylusHandwriting(int requestId);
+ void resetStylusHandwriting(int requestId);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index e8a2d810d563..15d7acfb6e0a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -416,13 +416,13 @@ public final class InputMethodPrivilegedOperations {
* @param requestId
*/
@AnyThread
- public void finishStylusHandwriting(int requestId) {
+ public void resetStylusHandwriting(int requestId) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- ops.finishStylusHandwriting(requestId);
+ ops.resetStylusHandwriting(requestId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 74749cc9bf0b..fd8534d45b2b 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -29,6 +29,8 @@ import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -60,6 +62,7 @@ import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import com.android.internal.R;
+import com.android.internal.protolog.common.ProtoLog;
import java.util.List;
@@ -98,10 +101,6 @@ public class TransitionAnimation {
private static final String DEFAULT_PACKAGE = "android";
- // TODO (b/215515255): remove once we full migrate to shell transitions
- private static final boolean SHELL_TRANSITIONS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
-
private final Context mContext;
private final String mTag;
@@ -256,9 +255,6 @@ public class TransitionAnimation {
resId = ent.array.getResourceId(animAttr, 0);
}
}
- if (!SHELL_TRANSITIONS_ENABLED) {
- resId = updateToLegacyIfNeeded(resId);
- }
resId = updateToTranslucentAnimIfNeeded(resId, transit);
if (ResourceId.isValid(resId)) {
return loadAnimationSafely(context, resId, mTag);
@@ -266,24 +262,6 @@ public class TransitionAnimation {
return null;
}
- /**
- * Replace animations that are not compatible with the legacy transition system with ones that
- * are compatible with it.
- * TODO (b/215515255): remove once we full migrate to shell transitions
- */
- private int updateToLegacyIfNeeded(int anim) {
- if (anim == R.anim.activity_open_enter) {
- return R.anim.activity_open_enter_legacy;
- } else if (anim == R.anim.activity_open_exit) {
- return R.anim.activity_open_exit_legacy;
- } else if (anim == R.anim.activity_close_enter) {
- return R.anim.activity_close_enter_legacy;
- } else if (anim == R.anim.activity_close_exit) {
- return R.anim.activity_close_exit_legacy;
- }
- return anim;
- }
-
/** Load animation by attribute Id from a specific AnimationStyle resource. */
@Nullable
public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
@@ -320,9 +298,9 @@ public class TransitionAnimation {
@Nullable
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
- Slog.v(mTag, "Loading animations: layout params pkg="
- + (lp != null ? lp.packageName : null)
- + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: layout params pkg=%s resId=0x%x",
+ lp != null ? lp.packageName : null,
+ lp != null ? lp.windowAnimations : 0);
}
if (lp != null && lp.windowAnimations != 0) {
// If this is a system resource, don't try to load it from the
@@ -334,7 +312,7 @@ public class TransitionAnimation {
packageName = DEFAULT_PACKAGE;
}
if (mDebug) {
- Slog.v(mTag, "Loading animations: picked package=" + packageName);
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: picked package=%s", packageName);
}
return AttributeCache.instance().get(packageName, resId,
com.android.internal.R.styleable.WindowAnimation);
@@ -345,16 +323,16 @@ public class TransitionAnimation {
@Nullable
private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
if (mDebug) {
- Slog.v(mTag, "Loading animations: package="
- + packageName + " resId=0x" + Integer.toHexString(resId));
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: package=%s resId=0x%x",
+ packageName, resId);
}
if (packageName != null) {
if ((resId & 0xFF000000) == 0x01000000) {
packageName = DEFAULT_PACKAGE;
}
if (mDebug) {
- Slog.v(mTag, "Loading animations: picked package="
- + packageName);
+ ProtoLog.v(WM_DEBUG_ANIM, "Loading animations: picked package=%s",
+ packageName);
}
return AttributeCache.instance().get(packageName, resId,
com.android.internal.R.styleable.WindowAnimation);
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 45c6d5f10a5a..7f36c79591b3 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -60,6 +60,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM),
WM_DEBUG_APP_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
+ WM_DEBUG_ANIM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
WM_DEBUG_APP_TRANSITIONS_ANIM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
WM_DEBUG_RECENTS_ANIMATIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 273c5f170812..40d89db6165c 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -67,4 +67,6 @@ oneway interface IInputMethod {
in List<MotionEvent> events);
void initInkWindow();
+
+ void finishStylusHandwriting();
}
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 361ba958f759..1074004b4c33 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -175,27 +175,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable {
return mCredential;
}
- /**
- * Returns the credential type recognized by {@link StorageManager}. Can be one of
- * {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
- * {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
- */
- public int getStorageCryptType() {
- if (isNone()) {
- return StorageManager.CRYPT_TYPE_DEFAULT;
- }
- if (isPattern()) {
- return StorageManager.CRYPT_TYPE_PATTERN;
- }
- if (isPin()) {
- return StorageManager.CRYPT_TYPE_PIN;
- }
- if (isPassword()) {
- return StorageManager.CRYPT_TYPE_PASSWORD;
- }
- throw new IllegalStateException("Unhandled credential type");
- }
-
/** Returns whether this is an empty credential */
public boolean isNone() {
ensureNotZeroized();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 45b6c786670d..4f9558885e40 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -471,11 +471,9 @@
android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
<!-- Defined in RestrictionsManager -->
- <protected-broadcast
- android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
- <!-- Defined in RestrictionsManager -->
+ <protected-broadcast android:name="android.content.action.PERMISSION_RESPONSE_RECEIVED" />
+ <protected-broadcast android:name="android.content.action.REQUEST_PERMISSION" />
- <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
<protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_STARTED" />
<protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
<protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_PROGRESS" />
diff --git a/core/res/res/anim/activity_close_enter_legacy.xml b/core/res/res/anim/activity_close_enter_legacy.xml
deleted file mode 100644
index 9fa7c5498ea6..000000000000
--- a/core/res/res/anim/activity_close_enter_legacy.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <scale
- android:fromXScale="1.1"
- android:toXScale="1"
- android:fromYScale="1.1"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
-</set> \ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit_legacy.xml b/core/res/res/anim/activity_close_exit_legacy.xml
deleted file mode 100644
index 1599ae8cb19f..000000000000
--- a/core/res/res/anim/activity_close_exit_legacy.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha
- android:fromAlpha="1"
- android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="33"
- android:duration="50"/>
- <scale
- android:fromXScale="1"
- android:toXScale="0.9"
- android:fromYScale="1"
- android:toYScale="0.9"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
-</set>
diff --git a/core/res/res/anim/activity_open_enter_legacy.xml b/core/res/res/anim/activity_open_enter_legacy.xml
deleted file mode 100644
index 38d3e8ed06ce..000000000000
--- a/core/res/res/anim/activity_open_enter_legacy.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <alpha
- android:fromAlpha="0"
- android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="50"
- android:duration="50"/>
- <scale
- android:fromXScale="0.85"
- android:toXScale="1"
- android:fromYScale="0.85"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
-</set>
diff --git a/core/res/res/anim/activity_open_exit_legacy.xml b/core/res/res/anim/activity_open_exit_legacy.xml
deleted file mode 100644
index 3865d2149f42..000000000000
--- a/core/res/res/anim/activity_open_exit_legacy.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-/*
-** Copyright 2009, 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
-
- <!-- Fade out, over a black surface, which simulates a black scrim -->
- <alpha
- android:fromAlpha="1"
- android:toAlpha="0.4"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="83"
- android:duration="167"/>
-
- <scale
- android:fromXScale="1"
- android:toXScale="1.05"
- android:fromYScale="1"
- android:toYScale="1.05"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
-</set> \ No newline at end of file
diff --git a/core/res/res/layout/app_language_picker_system_default.xml b/core/res/res/layout/app_language_picker_system_default.xml
new file mode 100644
index 000000000000..d9883bc3df04
--- /dev/null
+++ b/core/res/res/layout/app_language_picker_system_default.xml
@@ -0,0 +1,42 @@
+<!--
+ ~ 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="vertical"
+ android:paddingTop="8dp">
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/locale"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textDirection="locale"
+ android:layoutDirection="locale"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/system_locale_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2107f651eade..2295090b6616 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2322,7 +2322,9 @@
<attr name="windowSplashScreenAnimatedIcon" format="reference"/>
<!-- The duration, in milliseconds, of the window splash screen icon animation duration
when playing the splash screen starting window. The maximum animation duration should
- be limited below 1000ms. -->
+ be limited below 1000ms.
+ @deprecated Not used by framework starting from API level 33. The system estimates the
+ duration of the vector animation automatically. -->
<attr name="windowSplashScreenAnimationDuration" format="integer"/>
<!-- Place an drawable image in the bottom of the starting window, it can be used to
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0344a93571a5..94eb45cab8d6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2723,7 +2723,7 @@
<string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
<!-- Whether force to enable telephony new data stack or not -->
- <bool name="config_force_enable_telephony_new_data_stack">false</bool>
+ <bool name="config_force_enable_telephony_new_data_stack">true</bool>
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
@@ -2931,8 +2931,8 @@
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
- <!-- Settings intelligence package name -->
- <string name="config_settingsIntelligencePackageName" translatable="false">
+ <!-- System settings intelligence package name -->
+ <string name="config_systemSettingsIntelligence" translatable="false">
com.android.settings.intelligence
</string>
@@ -2963,6 +2963,11 @@
the currently focused view is a text editor. -->
<bool name="config_preventImeStartupUnlessTextEditor">false</bool>
+ <!-- These IMEs are known not to behave well when evicted from memory and thus are exempt
+ from the IME startup prevention behavior that is enabled by
+ config_preventImeStartupUnlessTextEditor. -->
+ <string-array name="config_nonPreemptibleInputMethods" translatable="false" />
+
<!-- The list of classes that should be added to the notification ranking pipeline.
See {@link com.android.server.notification.NotificationSignalExtractor}
If you add a new extractor to this list make sure to update
@@ -5803,4 +5808,14 @@
because of it.
-->
<bool name="config_bg_current_drain_high_threshold_by_bg_location">false</bool>
+
+ <!-- Start safety protection resources to be overlaid -->
+
+ <!-- Safety protection icon to be overlaid -->
+ <item name="ic_safety_protection" type="drawable">@null</item>
+
+ <!-- Display text for safety protection to be overlaid. This is translatable -->
+ <string name="safety_protection_display_text"></string>
+
+ <!-- End safety protection resources to be overlaid -->
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index e35d2e9f488a..2997fef00f2f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -174,6 +174,10 @@
<public name="config_systemAutomotiveCalendarSyncManager" />
<!-- @hide @SystemApi -->
<public name="config_defaultAutomotiveNavigation" />
+ <!-- @hide @SystemApi -->
+ <public name="safety_protection_display_text" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemSettingsIntelligence" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
@@ -188,6 +192,8 @@
</staging-public-group>
<staging-public-group type="drawable" first-id="0x01d80000">
+ <!-- @hide @SystemApi -->
+ <public name="ic_safety_protection" />
</staging-public-group>
<staging-public-group type="layout" first-id="0x01d70000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4c71b3abd00c..4a2c9119b745 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2988,9 +2988,6 @@
<!-- Displayed to the user to confirm that they have copied text from a web page to the clipboard. -->
<string name="text_copied">Text copied to clipboard.</string>
- <!-- Displayed to the user to confirm that they have copied text/images to the clipboard [CHAR LIMIT=NONE] -->
- <string name="copied">Copied</string>
-
<!-- Displayed to the user to inform them that an app has accessed clipboard data (pasted as in "copy and paste") that was copied from another app [CHAR LIMIT=50] -->
<string name="pasted_from_app"><xliff:g id="pasting_app_name" example="Gmail">%1$s</xliff:g> pasted from <xliff:g id="source_app_name" example="Chrome">%2$s</xliff:g></string>
@@ -6296,4 +6293,7 @@ ul.</string>
<!-- Strings for VirtualDeviceManager -->
<!-- Error message indicating the camera cannot be accessed when running on a virtual device. [CHAR LIMIT=NONE] -->
<string name="vdm_camera_access_denied">Cannot access camera from this device</string>
+
+ <!-- Title for preference of the system default locale. [CHAR LIMIT=50]-->
+ <string name="system_locale_title">System language</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1f0b22b9e460..845de8a3492e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1713,10 +1713,6 @@
<java-symbol type="anim" name="activity_open_exit" />
<java-symbol type="anim" name="activity_close_enter" />
<java-symbol type="anim" name="activity_close_exit" />
- <java-symbol type="anim" name="activity_open_enter_legacy" />
- <java-symbol type="anim" name="activity_open_exit_legacy" />
- <java-symbol type="anim" name="activity_close_enter_legacy" />
- <java-symbol type="anim" name="activity_close_exit_legacy" />
<java-symbol type="anim" name="task_fragment_close_enter" />
<java-symbol type="anim" name="task_fragment_close_exit" />
<java-symbol type="anim" name="task_fragment_open_enter" />
@@ -2269,6 +2265,7 @@
<java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="bool" name="config_killableInputMethods" />
<java-symbol type="bool" name="config_preventImeStartupUnlessTextEditor" />
+ <java-symbol type="array" name="config_nonPreemptibleInputMethods" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -4649,8 +4646,6 @@
<java-symbol type="drawable" name="ic_accessibility_24dp" />
<java-symbol type="string" name="view_and_control_notification_title" />
<java-symbol type="string" name="view_and_control_notification_content" />
- <java-symbol type="array" name="config_accessibility_allowed_install_source" />
-
<!-- Translation -->
<java-symbol type="string" name="ui_translation_accessibility_translated_text" />
<java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
@@ -4772,4 +4767,7 @@
<java-symbol type="drawable" name="ic_swap_horiz" />
<java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" />
+ <java-symbol type="string" name="system_locale_title" />
+ <java-symbol type="layout" name="app_language_picker_system_default" />
+ <java-symbol type="id" name="system_locale_subtitle" />
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
index cd61011c856a..863aac35c0f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -45,7 +45,13 @@ import org.mockito.MockitoAnnotations;
public class BatteryStatsHistoryTest {
private static final String TAG = "BatteryStatsHistoryTest";
private static final int MAX_HISTORY_FILES = 32;
- private final BatteryStatsImpl mBatteryStatsImpl = new MockBatteryStatsImpl();
+ private static final int MAX_HISTORY_BUFFER_KB = 128;
+
+ // Initializing max history files and buffer to the default values of non-low-ram device
+ // to maintain consistency in the tests
+ private final BatteryStatsImpl mBatteryStatsImpl = new MockBatteryStatsImpl()
+ .setMaxHistoryFiles(MAX_HISTORY_FILES)
+ .setMaxHistoryBuffer(MAX_HISTORY_BUFFER_KB * 1024);
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
private File mHistoryDir;
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 1bb41a8cfffd..00154a3d23b0 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -26,6 +26,7 @@ import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
@@ -193,6 +194,18 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return this;
}
+ @GuardedBy("this")
+ public MockBatteryStatsImpl setMaxHistoryFiles(int maxHistoryFiles) {
+ mConstants.MAX_HISTORY_FILES = maxHistoryFiles;
+ return this;
+ }
+
+ @GuardedBy("this")
+ public MockBatteryStatsImpl setMaxHistoryBuffer(int maxHistoryBuffer) {
+ mConstants.MAX_HISTORY_BUFFER = maxHistoryBuffer;
+ return this;
+ }
+
public int getAndClearExternalStatsSyncFlags() {
final int flags = mExternalStatsSync.flags;
mExternalStatsSync.flags = 0;
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 4449c48d28bb..99cb40a7fce7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -19,6 +19,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "-2111539867": {
+ "message": "remove IME snapshot, caller=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-2109936758": {
"message": "removeAppToken make exiting: %s",
"level": "VERBOSE",
@@ -61,6 +67,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-2052051397": {
+ "message": "Clear animatingExit: reason=destroySurface win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-2049725903": {
"message": "Task back pressed on root taskId=%d",
"level": "VERBOSE",
@@ -127,6 +139,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-1969928125": {
+ "message": "Animation start for %s, anim=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"-1963461591": {
"message": "Removing %s from %s",
"level": "VERBOSE",
@@ -163,6 +181,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1933723759": {
+ "message": "Clear animatingExit: reason=relayoutVisibleWindow win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1924376693": {
"message": " Setting Ready-group to %b. group=%s from %s",
"level": "VERBOSE",
@@ -265,6 +289,12 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-1814361639": {
+ "message": "Set IME snapshot position: (%d, %d)",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-1810446914": {
"message": "Trying to update display configuration for system\/invalid process.",
"level": "WARN",
@@ -307,6 +337,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-1777010776": {
+ "message": "create IME snapshot for %s, buff width=%s, height=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-1770075711": {
"message": "Adding window client %s that is dead, aborting.",
"level": "WARN",
@@ -613,6 +649,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1471518109": {
+ "message": "Set animatingExit: reason=onAppVisibilityChanged win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1468740466": {
"message": "Moving to PAUSED: %s (starting in paused state)",
"level": "VERBOSE",
@@ -751,6 +793,12 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
+ "-1318478129": {
+ "message": "applyAnimation: win=%s anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-1311436264": {
"message": "Unregister task fragment organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
@@ -781,12 +829,24 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1303628829": {
+ "message": "**** STARTING EXIT",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
"-1292329638": {
"message": "Added starting %s: startingWindow=%s startingView=%s",
"level": "VERBOSE",
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1288007399": {
+ "message": "performShowLocked: mDrawState=HAS_DRAWN in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1270731689": {
"message": "Attempted to set replacing window on app token with no content %s",
"level": "WARN",
@@ -835,6 +895,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
+ "-1209252064": {
+ "message": "Clear animatingExit: reason=clearAnimatingFlags win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1207757583": {
"message": "startAnimation(): Notify animation start: %s",
"level": "DEBUG",
@@ -1219,6 +1285,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-799003045": {
+ "message": "Set animatingExit: reason=remove\/replaceWindow win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-784959154": {
"message": "Attempted to add private presentation window to a non-private display. Aborting.",
"level": "WARN",
@@ -1375,6 +1447,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-658964693": {
+ "message": "onWindowAnimationFinished, wc=%s, type=%s, imeSnapshot=%s, target=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-655104359": {
"message": "Frontmost changed immersion: %s",
"level": "DEBUG",
@@ -1675,6 +1753,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-347866078": {
+ "message": "Setting move animation on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-344488673": {
"message": "Finishing drawing window %s: mDrawState=%s",
"level": "VERBOSE",
@@ -1699,6 +1783,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-319689203": {
+ "message": "Reparenting to original parent: %s for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"-317761482": {
"message": "Create sleep token: tag=%s, displayId=%d",
"level": "DEBUG",
@@ -1807,6 +1897,18 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-208664771": {
+ "message": "Reparenting to leash for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
+ "-203358733": {
+ "message": "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -1903,6 +2005,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-91393839": {
+ "message": "Set animatingExit: reason=remove\/applyAnimation win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-90559682": {
"message": "Config is skipping already pausing %s",
"level": "VERBOSE",
@@ -1927,6 +2035,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/Session.java"
},
+ "-81121442": {
+ "message": "ImeContainer just became organized but it doesn't have a parent or the parent doesn't have a surface control. mSurfaceControl=%s imeParentSurfaceControl=%s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-80004683": {
"message": "Resume failed; resetting state to %s: %s",
"level": "VERBOSE",
@@ -1939,6 +2053,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
+ "-57750640": {
+ "message": "show IME snapshot, ime target=%s, callers=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-55185509": {
"message": "setFocusedTask: taskId=%d touchedActivity=%s",
"level": "DEBUG",
@@ -1957,6 +2077,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "-32102932": {
+ "message": "Error sending initial configuration change to WindowContainer overlay",
+ "level": "ERROR",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"-23020844": {
"message": "Back: Reset surfaces",
"level": "DEBUG",
@@ -2191,6 +2317,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "215077284": {
+ "message": "Animation start delayed for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"221540118": {
"message": "mUserActivityTimeout set to %d",
"level": "DEBUG",
@@ -2281,6 +2413,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
+ "283489582": {
+ "message": "Clear animatingExit: reason=exitAnimationDone win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"288485303": {
"message": "Attempted to set remove mode to a display that does not exist: %d",
"level": "WARN",
@@ -2353,6 +2491,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "341360111": {
+ "message": "selectAnimation in %s: transit=%d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2419,6 +2563,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "385595355": {
+ "message": "Starting animation on %s: type=%d, anim=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"397105698": {
"message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
"level": "VERBOSE",
@@ -2431,6 +2581,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "397862437": {
+ "message": "Cancelling animation restarting=%b for %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/SurfaceAnimator.java"
+ },
"399841913": {
"message": "SURFACE RECOVER DESTROY: %s",
"level": "INFO",
@@ -2767,12 +2923,6 @@
"group": "WM_DEBUG_WALLPAPER",
"at": "com\/android\/server\/wm\/WallpaperWindowToken.java"
},
- "736003885": {
- "message": "Unable to retrieve the task token to start recording for display %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecorder.java"
- },
"736692676": {
"message": "Config is relaunching %s",
"level": "VERBOSE",
@@ -2803,6 +2953,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "769218938": {
+ "message": "Loaded animation %s for %s, duration: %d, stack=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"778774915": {
"message": "Unable to record task since feature is disabled %d",
"level": "VERBOSE",
@@ -2959,6 +3115,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "975275467": {
+ "message": "Set animatingExit: reason=remove\/isAnimating win=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"979347997": {
"message": "Launch on display check: disallow activity embedding without permission.",
"level": "DEBUG",
@@ -3127,6 +3289,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
},
+ "1164325516": {
+ "message": "onExitAnimationDone in %s: exiting=%b remove=%b selfAnimating=%b anim=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1166381079": {
"message": "Execute app transition: %s, displayId: %d Callers=%s",
"level": "WARN",
@@ -3139,6 +3307,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
+ "1175495463": {
+ "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_IME",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1178653181": {
"message": "Old wallpaper still the target.",
"level": "VERBOSE",
@@ -3265,6 +3439,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
+ "1335791109": {
+ "message": "createSurface %s: mDrawState=DRAW_PENDING",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1337596507": {
"message": "Sending to proc %s new compat %s",
"level": "VERBOSE",
@@ -3679,6 +3859,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1810209625": {
+ "message": "Animation done in %s: exiting=%b, reportedVisible=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1822314934": {
"message": "Expected target rootTask=%s to restored behind rootTask=%s but it is behind rootTask=%s",
"level": "WARN",
@@ -3757,6 +3943,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
},
+ "1878927091": {
+ "message": "prepareSurface: No changes in animation for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
@@ -3859,6 +4051,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/InputMonitor.java"
},
+ "2010476671": {
+ "message": "Animation done in %s: reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"2018454757": {
"message": "WS.removeImmediately: %s Already removed...",
"level": "VERBOSE",
@@ -3871,6 +4069,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "2019765997": {
+ "message": "selectRotationAnimation topFullscreen=%s rotationAnimation=%d forceJumpcut=%b",
+ "level": "INFO",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
"2022422429": {
"message": "createAnimationAdapter(): container=%s",
"level": "DEBUG",
@@ -3925,6 +4129,12 @@
"group": "WM_DEBUG_WINDOW_INSETS",
"at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
},
+ "2075693141": {
+ "message": "Set animatingExit: reason=startExitingAnimation\/%s win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"2083556954": {
"message": "Set mOrientationChanging of %s",
"level": "VERBOSE",
@@ -3984,6 +4194,9 @@
"WM_DEBUG_ADD_REMOVE": {
"tag": "WindowManager"
},
+ "WM_DEBUG_ANIM": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_APP_TRANSITIONS": {
"tag": "WindowManager"
},
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index c1addbf43937..bfa6ce5d36c5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -1000,7 +1000,7 @@ public class RippleDrawable extends LayerDrawable {
}
private int clampAlpha(@ColorInt int color) {
- if (Color.alpha(color) > 128) {
+ if (Color.alpha(color) < 128) {
return (color & 0x00FFFFFF) | 0x80000000;
}
return color;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 2aa695346c89..418ff0e7263a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -37,6 +37,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -58,14 +59,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Currently applied split configuration.
private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
- private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
- private final List<SplitContainer> mSplitContainers = new ArrayList<>();
+ /**
+ * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
+ * below it.
+ * When the app is host of multiple Tasks, there can be multiple splits controlled by the same
+ * organizer.
+ */
+ private final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
// Callback to Jetpack to notify about changes to split states.
private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
// We currently only support split activity embedding within the one root Task.
+ // TODO(b/207720388): move to TaskContainer
private final Rect mParentBounds = new Rect();
public SplitController() {
@@ -244,7 +251,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
} else {
// Put activity into a new expanded container
- final TaskFragmentContainer newContainer = newContainer(launchedActivity);
+ final TaskFragmentContainer newContainer = newContainer(launchedActivity,
+ launchedActivity.getTaskId());
mPresenter.expandActivity(newContainer.getTaskFragmentToken(),
launchedActivity);
}
@@ -327,12 +335,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@Nullable
TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
- for (TaskFragmentContainer container : mContainers) {
- if (container.hasActivity(activityToken)) {
- return container;
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ for (TaskFragmentContainer container : containers) {
+ if (container.hasActivity(activityToken)) {
+ return container;
+ }
}
}
-
return null;
}
@@ -340,9 +350,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
*/
- TaskFragmentContainer newContainer(@Nullable Activity activity) {
- TaskFragmentContainer container = new TaskFragmentContainer(activity);
- mContainers.add(container);
+ TaskFragmentContainer newContainer(@Nullable Activity activity, int taskId) {
+ final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskId);
+ if (!mTaskContainers.contains(taskId)) {
+ mTaskContainers.put(taskId, new TaskContainer());
+ }
+ mTaskContainers.get(taskId).mContainers.add(container);
return container;
}
@@ -354,13 +367,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
@NonNull SplitRule splitRule) {
- SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+ final SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
secondaryContainer, splitRule);
// Remove container later to prevent pinning escaping toast showing in lock task mode.
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
- mSplitContainers.add(splitContainer);
+ mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer);
}
/**
@@ -368,15 +381,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
void removeContainer(@NonNull TaskFragmentContainer container) {
// Remove all split containers that included this one
- mContainers.remove(container);
- List<SplitContainer> containersToRemove = new ArrayList<>();
- for (SplitContainer splitContainer : mSplitContainers) {
+ final int taskId = container.getTaskId();
+ final TaskContainer taskContainer = mTaskContainers.get(taskId);
+ if (taskContainer == null) {
+ return;
+ }
+ taskContainer.mContainers.remove(container);
+ if (taskContainer.mContainers.isEmpty()) {
+ mTaskContainers.remove(taskId);
+ // No more TaskFragment in this Task, so no need to check split container.
+ return;
+ }
+
+ final List<SplitContainer> containersToRemove = new ArrayList<>();
+ for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
if (container.equals(splitContainer.getSecondaryContainer())
|| container.equals(splitContainer.getPrimaryContainer())) {
containersToRemove.add(splitContainer);
}
}
- mSplitContainers.removeAll(containersToRemove);
+ taskContainer.mSplitContainers.removeAll(containersToRemove);
}
/**
@@ -399,12 +423,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
- * Returns the topmost not finished container.
+ * Returns the topmost not finished container in Task of given task id.
*/
@Nullable
- TaskFragmentContainer getTopActiveContainer() {
- for (int i = mContainers.size() - 1; i >= 0; i--) {
- TaskFragmentContainer container = mContainers.get(i);
+ TaskFragmentContainer getTopActiveContainer(int taskId) {
+ final TaskContainer taskContainer = mTaskContainers.get(taskId);
+ if (taskContainer == null) {
+ return null;
+ }
+ for (int i = taskContainer.mContainers.size() - 1; i >= 0; i--) {
+ final TaskFragmentContainer container = taskContainer.mContainers.get(i);
if (!container.isFinished() && container.getRunningActivityCount() > 0) {
return container;
}
@@ -434,7 +462,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (splitContainer == null) {
return;
}
- if (splitContainer != mSplitContainers.get(mSplitContainers.size() - 1)) {
+ final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId())
+ .mSplitContainers;
+ if (splitContainers == null
+ || splitContainer != splitContainers.get(splitContainers.size() - 1)) {
// Skip position update - it isn't the topmost split.
return;
}
@@ -455,8 +486,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@Nullable
private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) {
- for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
- SplitContainer splitContainer = mSplitContainers.get(i);
+ final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId())
+ .mSplitContainers;
+ if (splitContainers == null) {
+ return null;
+ }
+ for (int i = splitContainers.size() - 1; i >= 0; i--) {
+ final SplitContainer splitContainer = splitContainers.get(i);
if (container.equals(splitContainer.getSecondaryContainer())
|| container.equals(splitContainer.getPrimaryContainer())) {
return splitContainer;
@@ -473,8 +509,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private SplitContainer getActiveSplitForContainers(
@NonNull TaskFragmentContainer firstContainer,
@NonNull TaskFragmentContainer secondContainer) {
- for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
- SplitContainer splitContainer = mSplitContainers.get(i);
+ final List<SplitContainer> splitContainers = mTaskContainers.get(firstContainer.getTaskId())
+ .mSplitContainers;
+ if (splitContainers == null) {
+ return null;
+ }
+ for (int i = splitContainers.size() - 1; i >= 0; i--) {
+ final SplitContainer splitContainer = splitContainers.get(i);
final TaskFragmentContainer primary = splitContainer.getPrimaryContainer();
final TaskFragmentContainer secondary = splitContainer.getSecondaryContainer();
if ((firstContainer == secondary && secondContainer == primary)
@@ -501,7 +542,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer container = getContainerWithActivity(
activity.getActivityToken());
// Don't launch placeholder if the container is occluded.
- if (container != null && container != getTopActiveContainer()) {
+ if (container != null && container != getTopActiveContainer(container.getTaskId())) {
return false;
}
@@ -588,24 +629,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Nullable
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
- for (SplitContainer container : mSplitContainers) {
- if (container.getPrimaryContainer().isEmpty()
- || container.getSecondaryContainer().isEmpty()) {
- // We are in an intermediate state because either the split container is about to be
- // removed or the primary or secondary container are about to receive an activity.
- return null;
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<SplitContainer> splitContainers = mTaskContainers.valueAt(i)
+ .mSplitContainers;
+ for (SplitContainer container : splitContainers) {
+ if (container.getPrimaryContainer().isEmpty()
+ || container.getSecondaryContainer().isEmpty()) {
+ // We are in an intermediate state because either the split container is about
+ // to be removed or the primary or secondary container are about to receive an
+ // activity.
+ return null;
+ }
+ final ActivityStack primaryContainer = container.getPrimaryContainer()
+ .toActivityStack();
+ final ActivityStack secondaryContainer = container.getSecondaryContainer()
+ .toActivityStack();
+ final SplitInfo splitState = new SplitInfo(primaryContainer, secondaryContainer,
+ // Splits that are not showing side-by-side are reported as having 0 split
+ // ratio, since by definition in the API the primary container occupies no
+ // width of the split when covered by the secondary.
+ mPresenter.shouldShowSideBySide(container)
+ ? container.getSplitRule().getSplitRatio()
+ : 0.0f);
+ splitStates.add(splitState);
}
- ActivityStack primaryContainer = container.getPrimaryContainer().toActivityStack();
- ActivityStack secondaryContainer = container.getSecondaryContainer().toActivityStack();
- SplitInfo splitState = new SplitInfo(primaryContainer,
- secondaryContainer,
- // Splits that are not showing side-by-side are reported as having 0 split
- // ratio, since by definition in the API the primary container occupies no
- // width of the split when covered by the secondary.
- mPresenter.shouldShowSideBySide(container)
- ? container.getSplitRule().getSplitRatio()
- : 0.0f);
- splitStates.add(splitState);
}
return splitStates;
}
@@ -615,11 +662,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* the client.
*/
private boolean allActivitiesCreated() {
- for (TaskFragmentContainer container : mContainers) {
- if (container.getInfo() == null
- || container.getInfo().getActivities().size()
- != container.collectActivities().size()) {
- return false;
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ for (TaskFragmentContainer container : containers) {
+ if (container.getInfo() == null
+ || container.getInfo().getActivities().size()
+ != container.collectActivities().size()) {
+ return false;
+ }
}
}
return true;
@@ -633,7 +683,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (container == null) {
return false;
}
- for (SplitContainer splitContainer : mSplitContainers) {
+ final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId())
+ .mSplitContainers;
+ if (splitContainers == null) {
+ return true;
+ }
+ for (SplitContainer splitContainer : splitContainers) {
if (container.equals(splitContainer.getPrimaryContainer())
|| container.equals(splitContainer.getSecondaryContainer())) {
return false;
@@ -684,9 +739,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Nullable
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
- for (TaskFragmentContainer container : mContainers) {
- if (container.getTaskFragmentToken().equals(fragmentToken)) {
- return container;
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ for (TaskFragmentContainer container : containers) {
+ if (container.getTaskFragmentToken().equals(fragmentToken)) {
+ return container;
+ }
}
}
return null;
@@ -969,4 +1027,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Not reuse if it needs to destroy the existing.
return !pairRule.shouldClearTop();
}
+
+ /** Represents TaskFragments and split pairs below a Task. */
+ private static class TaskContainer {
+ final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+ final List<SplitContainer> mSplitContainers = new ArrayList<>();
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index ade573132eef..e7552ff48d52 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -80,7 +80,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
container.finish(shouldFinishDependent, this, wct, mController);
- final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer();
+ final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer(
+ container.getTaskId());
if (newTopContainer != null) {
mController.updateContainer(wct, newTopContainer);
}
@@ -103,7 +104,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- final TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(null,
+ primaryContainer.getTaskId());
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
@@ -159,7 +161,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* Creates a new expanded container.
*/
TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
- final TaskFragmentContainer newContainer = mController.newContainer(null);
+ final TaskFragmentContainer newContainer = mController.newContainer(null,
+ launchingActivity.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
createTaskFragment(wct, newContainer.getTaskFragmentToken(),
@@ -180,7 +183,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
TaskFragmentContainer container = mController.getContainerWithActivity(
activity.getActivityToken());
if (container == null || container == containerToAvoid) {
- container = mController.newContainer(activity);
+ container = mController.newContainer(activity, activity.getTaskId());
final TaskFragmentCreationParams fragmentOptions =
createFragmentOptions(
@@ -222,10 +225,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
launchingActivity.getActivityToken());
if (primaryContainer == null) {
- primaryContainer = mController.newContainer(launchingActivity);
+ primaryContainer = mController.newContainer(launchingActivity,
+ launchingActivity.getTaskId());
}
- TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ TaskFragmentContainer secondaryContainer = mController.newContainer(null,
+ primaryContainer.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 4d2d0551d828..e49af41d4eac 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -16,6 +16,8 @@
package androidx.window.extensions.embedding;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -41,6 +43,9 @@ class TaskFragmentContainer {
@NonNull
private final IBinder mToken;
+ /** Parent leaf Task id. */
+ private final int mTaskId;
+
/**
* Server-provided task fragment information.
*/
@@ -71,8 +76,12 @@ class TaskFragmentContainer {
* Creates a container with an existing activity that will be re-parented to it in a window
* container transaction.
*/
- TaskFragmentContainer(@Nullable Activity activity) {
+ TaskFragmentContainer(@Nullable Activity activity, int taskId) {
mToken = new Binder("TaskFragmentContainer");
+ if (taskId == INVALID_TASK_ID) {
+ throw new IllegalArgumentException("Invalid Task id");
+ }
+ mTaskId = taskId;
if (activity != null) {
addPendingAppearedActivity(activity);
}
@@ -275,6 +284,11 @@ class TaskFragmentContainer {
}
}
+ /** Gets the parent leaf Task id. */
+ int getTaskId() {
+ return mTaskId;
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
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 13d12b3589c0..677c1c7d7759 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
@@ -2802,7 +2802,14 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setVisibility(View.INVISIBLE);
mExpandedViewContainer.setAlpha(0f);
mExpandedViewContainer.addView(bev);
- bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
+
+ postDelayed(() -> {
+ // Set the Manage button click handler from postDelayed. This appears to resolve
+ // a race condition with adding the BubbleExpandedView view to the expanded view
+ // container. Due to the race condition the click handler sometimes is not set up
+ // correctly and is never called.
+ bev.setManageClickListener((view) -> showManageMenu(true /* show */));
+ }, 0);
if (!mIsExpansionAnimating) {
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index cc3a3b2206f3..35f1038a6853 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -51,7 +51,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
/**
* The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
- * Education for specific packages and which user has seen the full dialog for any package.
+ * Education dialog.
*/
@VisibleForTesting
static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
@@ -66,6 +66,13 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
private final Transitions mTransitions;
+ /**
+ * The id of the current user, to associate with a boolean in {@link
+ * #HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME}, indicating whether that user has already seen the
+ * Letterbox Education dialog.
+ */
+ private final int mUserId;
+
// Remember the last reported state in case visibility changes due to keyguard or IME updates.
private boolean mEligibleForLetterboxEducation;
@@ -98,6 +105,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
mTransitions = transitions;
mOnDismissCallback = onDismissCallback;
mAnimationController = animationController;
+ mUserId = taskInfo.userId;
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
Context.MODE_PRIVATE);
@@ -133,7 +141,6 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
@Override
protected View createLayout() {
- setSeenLetterboxEducation();
mLayout = inflateLayout();
updateDialogMargins();
@@ -177,6 +184,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
// Dialog has already been released.
return;
}
+ setSeenLetterboxEducation();
mLayout.setDismissOnClickListener(this::onDismiss);
// Focus on the dialog title for accessibility.
mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
@@ -241,7 +249,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
}
private String getPrefKey() {
- return String.valueOf(mContext.getUserId());
+ return String.valueOf(mUserId);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 917eaa061d1b..46b8e6098273 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -234,11 +234,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
setState(STATE_PIP_MENU);
+ mTvPipMenuController.showMenu();
updatePinnedStackBounds();
}
@Override
- public void closeMenu() {
+ public void onMenuClosed() {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: closeMenu(), state before=%s", TAG, stateToName(mState));
@@ -285,6 +286,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
@Override
+ public void enterPipMovementMenu() {
+ setState(STATE_PIP_MENU);
+ mTvPipMenuController.showMovementMenuOnly();
+ }
+
+ @Override
public void movePip(int keycode) {
if (mTvPipBoundsAlgorithm.updateGravity(keycode)) {
mTvPipMenuController.updateGravity(mTvPipBoundsState.getTvPipGravity());
@@ -438,7 +445,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
mPipNotificationController.dismiss();
- mTvPipMenuController.hideMenu();
+ mTvPipMenuController.closeMenu();
mTvPipBoundsState.resetTvPipState();
setState(STATE_NO_PIP);
mPinnedTaskId = NONEXISTENT_TASK_ID;
@@ -478,16 +485,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
TAG, stateToName(state), stateToName(mState));
}
mState = state;
-
- if (mState == STATE_PIP_MENU) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: > show menu", TAG);
- }
- mTvPipMenuController.showMenu();
- }
-
- updatePinnedStackBounds();
}
private void loadConfigurations() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b6ae398c1eb9..35c34ac8315f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -65,6 +65,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
// User can actively move the PiP via the DPAD.
private boolean mInMoveMode;
+ // Used when only showing the move menu since we want to close the menu completely when
+ // exiting the move menu instead of showing the regular button menu.
+ private boolean mCloseAfterExitMoveMenu;
private final List<RemoteAction> mMediaActions = new ArrayList<>();
private final List<RemoteAction> mAppActions = new ArrayList<>();
@@ -102,7 +105,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
final BroadcastReceiver closeSystemDialogsBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- hideMenu();
+ closeMenu();
}
};
context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver,
@@ -155,29 +158,49 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
0, SHELL_ROOT_LAYER_PIP);
}
+ void showMovementMenuOnly() {
+ if (DEBUG) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: showMovementMenuOnly()", TAG);
+ }
+ mInMoveMode = true;
+ mCloseAfterExitMoveMenu = true;
+ showMenuInternal();
+ }
+
@Override
public void showMenu() {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: showMenu()", TAG);
}
+ mInMoveMode = false;
+ mCloseAfterExitMoveMenu = false;
+ showMenuInternal();
+ }
- if (mPipMenuView != null) {
- Rect menuBounds = getMenuBounds(mTvPipBoundsState.getBounds());
- mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
- MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
- maybeUpdateMenuViewActions();
- updateExpansionState();
-
- SurfaceControl menuSurfaceControl = getSurfaceControl();
- if (menuSurfaceControl != null) {
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
- t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
- t.apply();
- }
- grantPipMenuFocus(true);
- mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
+ private void showMenuInternal() {
+ if (mPipMenuView == null) {
+ return;
+ }
+ Rect menuBounds = getMenuBounds(mTvPipBoundsState.getBounds());
+ mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
+ MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
+ maybeUpdateMenuViewActions();
+ updateExpansionState();
+
+ SurfaceControl menuSurfaceControl = getSurfaceControl();
+ if (menuSurfaceControl != null) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
+ t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
+ t.apply();
+ }
+ grantPipMenuFocus(true);
+ if (mInMoveMode) {
+ mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
+ } else {
+ mPipMenuView.showButtonMenu();
}
}
@@ -199,25 +222,18 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
return menuBounds;
}
- void hideMenu() {
- if (!isMenuVisible()) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: hideMenu() - Menu isn't visible, so don't hide", TAG);
- }
+ void closeMenu() {
+ if (DEBUG) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: closeMenu()", TAG);
+ }
+ if (mPipMenuView == null) {
return;
- } else {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: hideMenu()", TAG);
- }
}
- mPipMenuView.hide();
- if (!mInMoveMode) {
- grantPipMenuFocus(false);
- mDelegate.closeMenu();
- }
+ mPipMenuView.hideAll();
+ grantPipMenuFocus(false);
+ mDelegate.onMenuClosed();
}
boolean isInMoveMode() {
@@ -228,25 +244,29 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
public void onEnterMoveMode() {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onEnterMoveMode - %b", TAG, mInMoveMode);
+ "%s: onEnterMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
+ mCloseAfterExitMoveMenu);
}
mInMoveMode = true;
- mPipMenuView.showMenuButtons(false);
- mPipMenuView.showMovementHints(mDelegate.getPipGravity());
- mDelegate.onInMoveModeChanged();
+ mPipMenuView.showMoveMenu(mDelegate.getPipGravity());
}
@Override
public boolean onExitMoveMode() {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: onExitMoveMode - %b", TAG, mInMoveMode);
+ "%s: onExitMoveMode - %b, close when exiting move menu: %b", TAG, mInMoveMode,
+ mCloseAfterExitMoveMenu);
+ }
+ if (mCloseAfterExitMoveMenu) {
+ mInMoveMode = false;
+ mCloseAfterExitMoveMenu = false;
+ closeMenu();
+ return true;
}
if (mInMoveMode) {
mInMoveMode = false;
- mPipMenuView.showMenuButtons(true);
- mPipMenuView.hideMovementHints();
- mDelegate.onInMoveModeChanged();
+ mPipMenuView.showButtonMenu();
return true;
}
return false;
@@ -266,7 +286,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void detach() {
- hideMenu();
+ closeMenu();
detachPipMenuView();
mLeash = null;
}
@@ -486,7 +506,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
@Override
public void onBackPress() {
if (!onExitMoveMode()) {
- hideMenu();
+ closeMenu();
}
}
@@ -516,7 +536,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
void togglePipExpansion();
- void closeMenu();
+ void onMenuClosed();
void closePip();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 7fdb9ed90875..ccd054aa7680 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -177,29 +177,43 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
expanded ? R.string.pip_collapse : R.string.pip_expand);
}
- void show(boolean inMoveMode, int gravity) {
+ /**
+ * @param gravity for the arrow hints
+ */
+ void showMoveMenu(int gravity) {
if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: show(), inMoveMode: %b", TAG, inMoveMode);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG);
}
- if (inMoveMode) {
- showMovementHints(gravity);
- } else {
- animateAlphaTo(1, mActionButtonsContainer);
+ showMenuButtons(false);
+ showMovementHints(gravity);
+ showMenuFrame(true);
+ }
+
+ void showButtonMenu() {
+ if (DEBUG) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showButtonMenu()", TAG);
}
- animateAlphaTo(1, mMenuFrameView);
+ showMenuButtons(true);
+ hideMovementHints();
+ showMenuFrame(true);
}
- void hide() {
+ /**
+ * Hides all menu views, including the menu frame.
+ */
+ void hideAll() {
if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: hide()", TAG);
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: hideAll()", TAG);
}
- animateAlphaTo(0, mActionButtonsContainer);
- animateAlphaTo(0, mMenuFrameView);
+ showMenuButtons(false);
hideMovementHints();
+ showMenuFrame(false);
}
private void animateAlphaTo(float alpha, View view) {
+ if (view.getAlpha() == alpha) {
+ return;
+ }
view.animate()
.alpha(alpha)
.setInterpolator(alpha == 0f ? TvPipInterpolators.EXIT : TvPipInterpolators.ENTER)
@@ -419,6 +433,10 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
}
+ private void showMenuFrame(boolean show) {
+ animateAlphaTo(show ? 1 : 0, mMenuFrameView);
+ }
+
interface Listener {
void onBackPress();
@@ -426,7 +444,10 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
void onEnterMoveMode();
/**
- * @return whether move mode was exited
+ * Called when a button for exiting move mode was pressed.
+ *
+ * @return true if the event was handled or false if the key event should be handled by the
+ * next receiver.
*/
boolean onExitMoveMode();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 7bd3ce9c45b2..4033f030b702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -56,6 +56,10 @@ public class TvPipNotificationController {
"com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
private static final String ACTION_CLOSE_PIP =
"com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
+ private static final String ACTION_MOVE_PIP =
+ "com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
+ private static final String ACTION_TOGGLE_EXPANDED_PIP =
+ "com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
private final Context mContext;
private final PackageManager mPackageManager;
@@ -222,6 +226,8 @@ public class TvPipNotificationController {
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(ACTION_CLOSE_PIP);
mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
+ mIntentFilter.addAction(ACTION_MOVE_PIP);
+ mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
}
boolean mRegistered = false;
@@ -252,6 +258,10 @@ public class TvPipNotificationController {
mDelegate.showPictureInPictureMenu();
} else if (ACTION_CLOSE_PIP.equals(action)) {
mDelegate.closePip();
+ } else if (ACTION_MOVE_PIP.equals(action)) {
+ mDelegate.enterPipMovementMenu();
+ } else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
+ mDelegate.togglePipExpansion();
}
}
}
@@ -259,5 +269,7 @@ public class TvPipNotificationController {
interface Delegate {
void showPictureInPictureMenu();
void closePip();
+ void enterPipMovementMenu();
+ void togglePipExpansion();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index f0fb69f43499..b6c8cffb9699 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -301,8 +301,6 @@ public class SplashscreenContentDrawer {
Color.TRANSPARENT);
attrs.mSplashScreenIcon = safeReturnAttrDefault((def) -> typedArray.getDrawable(
R.styleable.Window_windowSplashScreenAnimatedIcon), null);
- attrs.mAnimationDuration = safeReturnAttrDefault((def) -> typedArray.getInt(
- R.styleable.Window_windowSplashScreenAnimationDuration, def), 0);
attrs.mBrandingImage = safeReturnAttrDefault((def) -> typedArray.getDrawable(
R.styleable.Window_windowSplashScreenBrandingImage), null);
attrs.mIconBgColor = safeReturnAttrDefault((def) -> typedArray.getColor(
@@ -310,9 +308,8 @@ public class SplashscreenContentDrawer {
Color.TRANSPARENT);
typedArray.recycle();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
- "getWindowAttrs: window attributes color: %s, replace icon: %b, avd duration: %d",
- Integer.toHexString(attrs.mWindowBgColor), attrs.mSplashScreenIcon != null,
- attrs.mAnimationDuration);
+ "getWindowAttrs: window attributes color: %s, replace icon: %b",
+ Integer.toHexString(attrs.mWindowBgColor), attrs.mSplashScreenIcon != null);
}
/** Creates the wrapper with system theme to avoid unexpected styles from app. */
@@ -327,7 +324,6 @@ public class SplashscreenContentDrawer {
private Drawable mSplashScreenIcon = null;
private Drawable mBrandingImage = null;
private int mIconBgColor = Color.TRANSPARENT;
- private int mAnimationDuration = 0;
}
/**
@@ -401,16 +397,13 @@ public class SplashscreenContentDrawer {
SplashScreenView build() {
Drawable iconDrawable;
- final long animationDuration;
if (mSuggestType == STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
|| mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
// empty or legacy splash screen case
- animationDuration = 0;
mFinalIconSize = 0;
} else if (mTmpAttrs.mSplashScreenIcon != null) {
// Using the windowSplashScreenAnimatedIcon attribute
iconDrawable = mTmpAttrs.mSplashScreenIcon;
- animationDuration = mTmpAttrs.mAnimationDuration;
// There is no background below the icon, so scale the icon up
if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT
@@ -440,11 +433,9 @@ public class SplashscreenContentDrawer {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
createIconDrawable(new BitmapDrawable(bitmap), true);
}
- animationDuration = 0;
}
- return fillViewWithIcon(mFinalIconSize, mFinalIconDrawables, animationDuration,
- mUiThreadInitTask);
+ return fillViewWithIcon(mFinalIconSize, mFinalIconDrawables, mUiThreadInitTask);
}
private class ShapeIconFactory extends BaseIconFactory {
@@ -460,7 +451,7 @@ public class SplashscreenContentDrawer {
} else {
mFinalIconDrawables = SplashscreenIconDrawableFactory.makeIconDrawable(
mTmpAttrs.mIconBgColor, mThemeColor, iconDrawable, mDefaultIconSize,
- mFinalIconSize, mSplashscreenWorkerHandler, mSplashScreenExecutor);
+ mFinalIconSize, mSplashscreenWorkerHandler);
}
}
@@ -520,7 +511,7 @@ public class SplashscreenContentDrawer {
}
private SplashScreenView fillViewWithIcon(int iconSize, @Nullable Drawable[] iconDrawable,
- long animationDuration, Consumer<Runnable> uiThreadInitTask) {
+ Consumer<Runnable> uiThreadInitTask) {
Drawable foreground = null;
Drawable background = null;
if (iconDrawable != null) {
@@ -536,7 +527,6 @@ public class SplashscreenContentDrawer {
.setIconSize(iconSize)
.setIconBackground(background)
.setCenterViewDrawable(foreground)
- .setAnimationDurationMillis(animationDuration)
.setUiThreadInitConsumer(uiThreadInitTask)
.setAllowHandleSolidColor(mAllowHandleSolidColor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index fdd5a1578f41..5f52071bf950 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -44,7 +44,6 @@ import android.util.PathParser;
import android.window.SplashScreenView;
import com.android.internal.R;
-import com.android.wm.shell.common.ShellExecutor;
import java.util.function.LongConsumer;
@@ -63,15 +62,14 @@ public class SplashscreenIconDrawableFactory {
*/
static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
@NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
- Handler splashscreenWorkerHandler, ShellExecutor splashScreenExecutor) {
+ Handler splashscreenWorkerHandler) {
Drawable foreground;
Drawable background = null;
boolean drawBackground =
backgroundColor != Color.TRANSPARENT && backgroundColor != themeColor;
if (foregroundDrawable instanceof Animatable) {
- foreground = new AnimatableIconAnimateListener(foregroundDrawable,
- splashScreenExecutor);
+ foreground = new AnimatableIconAnimateListener(foregroundDrawable);
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
// If the icon is Adaptive, we already use the icon background.
drawBackground = false;
@@ -274,12 +272,9 @@ public class SplashscreenIconDrawableFactory {
private boolean mAnimationTriggered;
private AnimatorListenerAdapter mJankMonitoringListener;
private boolean mRunning;
- private long mDuration;
private LongConsumer mStartListener;
- private final ShellExecutor mSplashScreenExecutor;
- AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable,
- ShellExecutor splashScreenExecutor) {
+ AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable) {
super(foregroundDrawable);
Callback callback = new Callback() {
@Override
@@ -299,7 +294,6 @@ public class SplashscreenIconDrawableFactory {
}
};
mForegroundDrawable.setCallback(callback);
- mSplashScreenExecutor = splashScreenExecutor;
mAnimatableIcon = (Animatable) mForegroundDrawable;
}
@@ -309,9 +303,8 @@ public class SplashscreenIconDrawableFactory {
}
@Override
- public void prepareAnimate(long duration, LongConsumer startListener) {
+ public void prepareAnimate(LongConsumer startListener) {
stopAnimation();
- mDuration = duration;
mStartListener = startListener;
}
@@ -328,11 +321,11 @@ public class SplashscreenIconDrawableFactory {
mJankMonitoringListener.onAnimationCancel(null);
}
if (mStartListener != null) {
- mStartListener.accept(mDuration);
+ mStartListener.accept(0);
}
return;
}
- long animDuration = mDuration;
+ long animDuration = 0;
if (mAnimatableIcon instanceof AnimatedVectorDrawable
&& ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration() > 0) {
animDuration = ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration();
@@ -341,9 +334,8 @@ public class SplashscreenIconDrawableFactory {
animDuration = ((AnimationDrawable) mAnimatableIcon).getTotalDuration();
}
mRunning = true;
- mSplashScreenExecutor.executeDelayed(this::stopAnimation, animDuration);
if (mStartListener != null) {
- mStartListener.accept(Math.max(animDuration, 0));
+ mStartListener.accept(animDuration);
}
}
@@ -359,7 +351,6 @@ public class SplashscreenIconDrawableFactory {
@Override
public void stopAnimation() {
if (mRunning) {
- mSplashScreenExecutor.removeCallbacks(this::stopAnimation);
onAnimationEnd();
mJankMonitoringListener = null;
}
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 2aa63b3571ab..b0e44a15b0da 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
@@ -434,6 +434,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// If available use the background color provided through AnimationOptions
backgroundColorForTransition =
info.getAnimationOptions().getBackgroundColor();
+ } else if (a.getBackgroundColor() != 0) {
+ // Otherwise fallback on the background color provided through the animation
+ // definition.
+ backgroundColorForTransition = a.getBackgroundColor();
} else if (change.getBackgroundColor() != 0) {
// Otherwise default to the window's background color if provided through
// the theme as the background color for the animation - the top most window
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 4607d8acc63e..596100dcdead 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -143,9 +143,7 @@ public class CompatUIControllerTest extends ShellTestCase {
// Verify that the compat controls and letterbox education are updated with new size compat
// info.
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
- clearInvocations(mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
@@ -156,9 +154,7 @@ public class CompatUIControllerTest extends ShellTestCase {
true);
// Verify that compat controls and letterbox education are removed with null task listener.
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
- clearInvocations(mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
/* taskListener= */ null);
@@ -181,9 +177,7 @@ public class CompatUIControllerTest extends ShellTestCase {
eq(mMockTaskListener));
// Verify that the layout is created again.
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
- clearInvocations(mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
@@ -206,9 +200,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
- clearInvocations(mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
@@ -217,9 +209,7 @@ public class CompatUIControllerTest extends ShellTestCase {
true);
// Verify that the layout is created again.
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
- clearInvocations(mController);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
@@ -294,8 +284,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
// No update if the insets state is the same.
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
@@ -368,8 +357,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
// Verify button remains hidden after keyguard becomes not showing since IME is showing.
mController.onKeyguardShowingChanged(false);
@@ -395,8 +383,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
- clearInvocations(mMockCompatLayout);
- clearInvocations(mMockLetterboxEduLayout);
+ clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
// Verify button remains hidden after IME is hidden since keyguard is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
index 7d51b521a9fb..f3a8cf45b7f8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -75,6 +76,12 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class LetterboxEduWindowManagerTest extends ShellTestCase {
+ private static final int USER_ID_1 = 1;
+ private static final int USER_ID_2 = 2;
+
+ private static final String PREF_KEY_1 = String.valueOf(USER_ID_1);
+ private static final String PREF_KEY_2 = String.valueOf(USER_ID_2);
+
private static final int TASK_ID = 1;
private static final int TASK_WIDTH = 200;
@@ -98,9 +105,10 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Mock private Runnable mOnDismissCallback;
private SharedPreferences mSharedPreferences;
- private String mPrefKey;
@Nullable
- private Boolean mInitialPrefValue = null;
+ private Boolean mInitialPrefValue1 = null;
+ @Nullable
+ private Boolean mInitialPrefValue2 = null;
@Before
public void setUp() {
@@ -109,20 +117,28 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
mSharedPreferences = mContext.getSharedPreferences(
LetterboxEduWindowManager.HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
Context.MODE_PRIVATE);
- mPrefKey = String.valueOf(mContext.getUserId());
- if (mSharedPreferences.contains(mPrefKey)) {
- mInitialPrefValue = mSharedPreferences.getBoolean(mPrefKey, /* default= */ false);
- mSharedPreferences.edit().remove(mPrefKey).apply();
+ if (mSharedPreferences.contains(PREF_KEY_1)) {
+ mInitialPrefValue1 = mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false);
+ mSharedPreferences.edit().remove(PREF_KEY_1).apply();
+ }
+ if (mSharedPreferences.contains(PREF_KEY_2)) {
+ mInitialPrefValue2 = mSharedPreferences.getBoolean(PREF_KEY_2, /* default= */ false);
+ mSharedPreferences.edit().remove(PREF_KEY_2).apply();
}
}
@After
public void tearDown() {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- if (mInitialPrefValue == null) {
- editor.remove(mPrefKey);
+ if (mInitialPrefValue1 == null) {
+ editor.remove(PREF_KEY_1);
} else {
- editor.putBoolean(mPrefKey, mInitialPrefValue);
+ editor.putBoolean(PREF_KEY_1, mInitialPrefValue1);
+ }
+ if (mInitialPrefValue2 == null) {
+ editor.remove(PREF_KEY_2);
+ } else {
+ editor.putBoolean(PREF_KEY_2, mInitialPrefValue2);
}
editor.apply();
}
@@ -137,19 +153,9 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
}
@Test
- public void testCreateLayout_alreadyShownToUser_doesNotCreateLayout() {
- LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
- mSharedPreferences.edit().putBoolean(mPrefKey, true).apply();
-
- assertFalse(windowManager.createLayout(/* canShow= */ true));
-
- assertNull(windowManager.mLayout);
- }
-
- @Test
public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
- true, /* isTaskbarEduShowing= */ true);
+ true, USER_ID_1, /* isTaskbarEduShowing= */ true);
assertFalse(windowManager.createLayout(/* canShow= */ true));
@@ -162,7 +168,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
assertTrue(windowManager.createLayout(/* canShow= */ false));
- assertFalse(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+ assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
assertNull(windowManager.mLayout);
}
@@ -172,7 +178,6 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
assertTrue(windowManager.createLayout(/* canShow= */ true));
- assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
LetterboxEduDialogLayout layout = windowManager.mLayout;
assertNotNull(layout);
verify(mViewHost).setView(eq(layout), mWindowAttrsCaptor.capture());
@@ -183,6 +188,8 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
assertNotNull(dialogTitle);
spyOn(dialogTitle);
+ // The education shouldn't be marked as seen until enter animation is done.
+ assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
// Clicking the layout does nothing until enter animation is done.
layout.performClick();
verify(mAnimationController, never()).startExitAnimation(any(), any());
@@ -191,6 +198,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
verifyAndFinishEnterAnimation(layout);
+ assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
verify(dialogTitle).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
// Exit animation should start following a click on the layout.
layout.performClick();
@@ -208,12 +216,41 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testCreateLayout_alreadyShownToUser_createsLayoutForOtherUserOnly() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true,
+ USER_ID_1, /* isTaskbarEduShowing= */ false);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+ assertNotNull(windowManager.mLayout);
+ verifyAndFinishEnterAnimation(windowManager.mLayout);
+ assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
+
+ windowManager.release();
+ windowManager = createWindowManager(/* eligible= */ true,
+ USER_ID_1, /* isTaskbarEduShowing= */ false);
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+ assertNull(windowManager.mLayout);
+
+ clearInvocations(mTransitions, mAnimationController);
+
+ windowManager = createWindowManager(/* eligible= */ true,
+ USER_ID_2, /* isTaskbarEduShowing= */ false);
+
+ assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+ assertNotNull(windowManager.mLayout);
+ verifyAndFinishEnterAnimation(windowManager.mLayout);
+ assertTrue(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
+ }
+
+ @Test
public void testCreateLayout_windowManagerReleasedBeforeTransitionsIsIdle_doesNotStartAnim() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
assertTrue(windowManager.createLayout(/* canShow= */ true));
-
- assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+ assertNotNull(windowManager.mLayout);
verify(mTransitions).runOnIdle(mRunOnIdleCaptor.capture());
@@ -222,6 +259,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
mRunOnIdleCaptor.getValue().run();
verify(mAnimationController, never()).startEnterAnimation(any(), any());
+ assertFalse(mSharedPreferences.getBoolean(PREF_KEY_1, /* default= */ false));
}
@Test
@@ -233,7 +271,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
assertNotNull(layout);
assertTrue(windowManager.updateCompatInfo(
- createTaskInfo(/* eligible= */ true, new Rect(50, 25, 150, 75)),
+ createTaskInfo(/* eligible= */ true, USER_ID_1, new Rect(50, 25, 150, 75)),
mTaskListener, /* canShow= */ true));
verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
@@ -341,13 +379,13 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
}
private LetterboxEduWindowManager createWindowManager(boolean eligible) {
- return createWindowManager(eligible, /* isTaskbarEduShowing= */ false);
+ return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ false);
}
private LetterboxEduWindowManager createWindowManager(boolean eligible,
- boolean isTaskbarEduShowing) {
+ int userId, boolean isTaskbarEduShowing) {
LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
- createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener,
+ createTaskInfo(eligible, userId), mSyncTransactionQueue, mTaskListener,
createDisplayLayout(), mTransitions, mOnDismissCallback,
mAnimationController);
@@ -375,11 +413,16 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
}
private static TaskInfo createTaskInfo(boolean eligible) {
- return createTaskInfo(eligible, new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT));
+ return createTaskInfo(eligible, USER_ID_1);
+ }
+
+ private static TaskInfo createTaskInfo(boolean eligible, int userId) {
+ return createTaskInfo(eligible, userId, new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT));
}
- private static TaskInfo createTaskInfo(boolean eligible, Rect bounds) {
+ private static TaskInfo createTaskInfo(boolean eligible, int userId, Rect bounds) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.userId = userId;
taskInfo.taskId = TASK_ID;
taskInfo.topActivityEligibleForLetterboxEducation = eligible;
taskInfo.configuration.windowConfiguration.setBounds(bounds);
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index e14e8791447a..657d839c15aa 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -122,11 +122,12 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
mBiopMessageType = BIOP_MESSAGE_TYPE_STREAM;
mFileDescriptor = null;
mChildList = null;
- mEventIds = eventIds;
- mEventNames = eventNames;
- if (mEventIds.length != eventNames.length) {
+ if (!((eventIds != null && eventNames != null && eventIds.length == eventNames.length)
+ || (eventIds == null && eventNames == null))) {
throw new IllegalStateException("The size of eventIds and eventNames must be equal");
}
+ mEventIds = eventIds;
+ mEventNames = eventNames;
}
private DsmccResponse(@NonNull Parcel source) {
@@ -137,10 +138,13 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
case BIOP_MESSAGE_TYPE_DIRECTORY:
int childNum = source.readInt();
- mChildList = new ArrayList<>();
- for (int i = 0; i < childNum; i++) {
- mChildList.add(source.readString());
- }
+ if (childNum > 0) {
+ mChildList = new ArrayList<>();
+ for (int i = 0; i < childNum; i++) {
+ mChildList.add(source.readString());
+ }
+ } else
+ mChildList = null;
mFileDescriptor = null;
mEventIds = null;
mEventNames = null;
@@ -153,11 +157,16 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
break;
case BIOP_MESSAGE_TYPE_STREAM:
int eventNum = source.readInt();
- mEventIds = new int[eventNum];
- mEventNames = new String[eventNum];
- for (int i = 0; i < eventNum; i++) {
- mEventIds[i] = source.readInt();
- mEventNames[i] = source.readString();
+ if (eventNum > 0) {
+ mEventIds = new int[eventNum];
+ mEventNames = new String[eventNum];
+ for (int i = 0; i < eventNum; i++) {
+ mEventIds[i] = source.readInt();
+ mEventNames[i] = source.readString();
+ }
+ } else {
+ mEventIds = null;
+ mEventNames = null;
}
mChildList = null;
mFileDescriptor = null;
@@ -196,7 +205,7 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
&& !mBiopMessageType.equals(BIOP_MESSAGE_TYPE_SERVICE_GATEWAY)) {
throw new IllegalStateException("Not directory object");
}
- return new ArrayList<String>(mChildList);
+ return mChildList != null ? new ArrayList<String>(mChildList) : new ArrayList<String>();
}
/**
@@ -207,7 +216,7 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
throw new IllegalStateException("Not stream event object");
}
- return mEventIds;
+ return mEventIds != null ? mEventIds : new int[0];
}
/**
@@ -218,7 +227,7 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
throw new IllegalStateException("Not stream event object");
}
- return mEventNames;
+ return mEventNames != null ? mEventNames : new String[0];
}
@Override
@@ -233,20 +242,26 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
switch (mBiopMessageType) {
case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
case BIOP_MESSAGE_TYPE_DIRECTORY:
- dest.writeInt(mChildList.size());
- for (String child : mChildList) {
- dest.writeString(child);
- }
+ if (mChildList != null && mChildList.size() > 0) {
+ dest.writeInt(mChildList.size());
+ for (String child : mChildList) {
+ dest.writeString(child);
+ }
+ } else
+ dest.writeInt(0);
break;
case BIOP_MESSAGE_TYPE_FILE:
dest.writeFileDescriptor(mFileDescriptor.getFileDescriptor());
break;
case BIOP_MESSAGE_TYPE_STREAM:
- dest.writeInt(mEventIds.length);
- for (int i = 0; i < mEventIds.length; i++) {
- dest.writeInt(mEventIds[i]);
- dest.writeString(mEventNames[i]);
- }
+ if (mEventIds != null && mEventIds.length > 0) {
+ dest.writeInt(mEventIds.length);
+ for (int i = 0; i < mEventIds.length; i++) {
+ dest.writeInt(mEventIds[i]);
+ dest.writeString(mEventNames[i]);
+ }
+ } else
+ dest.writeInt(0);
break;
default:
throw new IllegalStateException("unexpected BIOP message type");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4f7b7115fe26..2f4dd8fad8bb 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1003,19 +1003,9 @@ DrmPlugin::SecurityLevel jintToSecurityLevel(jint jlevel) {
}
static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
+ sp<IDrm> drm = android::DrmUtils::MakeDrm();
std::vector<uint8_t> bv;
- for (auto &factory : DrmUtils::MakeDrmFactories()) {
- sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
- if (factoryV1_3 == nullptr) {
- continue;
- }
- factoryV1_3->getSupportedCryptoSchemes(
- [&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes) {
- for (const auto &scheme : schemes) {
- bv.insert(bv.end(), scheme.data(), scheme.data() + scheme.size());
- }
- });
- }
+ drm->getSupportedSchemes(bv);
jbyteArray jUuidBytes = env->NewByteArray(bv.size());
env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 0bdf65d8ef55..5eeb167ee5e0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -20,6 +20,12 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
+import static android.companion.CompanionDeviceManager.REASON_CANCELED;
+import static android.companion.CompanionDeviceManager.REASON_DISCOVERY_TIMEOUT;
+import static android.companion.CompanionDeviceManager.REASON_USER_REJECTED;
+import static android.companion.CompanionDeviceManager.RESULT_DISCOVERY_TIMEOUT;
+import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
+import static android.companion.CompanionDeviceManager.RESULT_USER_REJECTED;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
@@ -89,9 +95,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private static final String FRAGMENT_DIALOG_TAG = "fragment_dialog";
- // Activity result: Internal Error.
- private static final int RESULT_INTERNAL_ERROR = 2;
-
// AssociationRequestsProcessor -> UI
private static final int RESULT_CODE_ASSOCIATION_CREATED = 0;
private static final String EXTRA_ASSOCIATION = "association";
@@ -209,7 +212,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// TODO: handle config changes without cancelling.
if (!isDone()) {
- cancel(false); // will finish()
+ cancel(/* discoveryTimeOut */ false, /* userRejected */ false); // will finish()
}
}
@@ -293,7 +296,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private void onDiscoveryStateChanged(DiscoveryState newState) {
if (newState == FINISHED_TIMEOUT
&& CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
- cancel(true);
+ cancel(/* discoveryTimeOut */ true, /* userRejected */ false);
}
}
@@ -336,10 +339,12 @@ public class CompanionDeviceActivity extends FragmentActivity implements
setResultAndFinish(association, RESULT_OK);
}
- private void cancel(boolean discoveryTimeout) {
+ private void cancel(boolean discoveryTimeout, boolean userRejected) {
if (DEBUG) {
- Log.i(TAG, "cancel(), discoveryTimeout=" + discoveryTimeout,
- new Exception("Stack Trace Dump"));
+ Log.i(TAG, "cancel(), discoveryTimeout="
+ + discoveryTimeout
+ + ", userRejected="
+ + userRejected, new Exception("Stack Trace Dump"));
}
if (isDone()) {
@@ -353,14 +358,27 @@ public class CompanionDeviceActivity extends FragmentActivity implements
CompanionDeviceDiscoveryService.stop(this);
}
+ final String cancelReason;
+ final int resultCode;
+ if (userRejected) {
+ cancelReason = REASON_USER_REJECTED;
+ resultCode = RESULT_USER_REJECTED;
+ } else if (discoveryTimeout) {
+ cancelReason = REASON_DISCOVERY_TIMEOUT;
+ resultCode = RESULT_DISCOVERY_TIMEOUT;
+ } else {
+ cancelReason = REASON_CANCELED;
+ resultCode = RESULT_CANCELED;
+ }
+
// First send callback to the app directly...
try {
- mAppCallback.onFailure(discoveryTimeout ? "Timeout." : "Cancelled.");
+ mAppCallback.onFailure(cancelReason);
} catch (RemoteException ignore) {
}
// ... then set result and finish ("sending" onActivityResult()).
- setResultAndFinish(null, RESULT_CANCELED);
+ setResultAndFinish(null, resultCode);
}
private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
@@ -562,7 +580,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
// Disable the button, to prevent more clicks.
v.setEnabled(false);
- cancel(false);
+ cancel(/* discoveryTimeout */ false, /* userRejected */ true);
}
private void onShowHelperDialog(View view) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 512fbcee9330..209f372fd377 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -45,6 +45,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* The Network Service Discovery Manager class provides the API to discover services
@@ -285,8 +286,12 @@ public final class NsdManager {
private final Context mContext;
private int mListenerKey = FIRST_LISTENER_KEY;
+ @GuardedBy("mMapLock")
private final SparseArray mListenerMap = new SparseArray();
+ @GuardedBy("mMapLock")
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
+ @GuardedBy("mMapLock")
+ private final SparseArray<Executor> mExecutorMap = new SparseArray<>();
private final Object mMapLock = new Object();
// Map of listener key sent by client -> per-network discovery tracker
@GuardedBy("mPerNetworkDiscoveryMap")
@@ -299,6 +304,7 @@ public final class NsdManager {
final String mServiceType;
final int mProtocolType;
final DiscoveryListener mBaseListener;
+ final Executor mBaseExecutor;
final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners =
new ArrayMap<>();
@@ -308,7 +314,8 @@ public final class NsdManager {
final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener(
network, mBaseListener);
mPerNetworkListeners.put(network, wrappedListener);
- discoverServices(mServiceType, mProtocolType, network, wrappedListener);
+ discoverServices(mServiceType, mProtocolType, network, mBaseExecutor,
+ wrappedListener);
}
@Override
@@ -355,9 +362,10 @@ public final class NsdManager {
}
private PerNetworkDiscoveryTracker(String serviceType, int protocolType,
- DiscoveryListener baseListener) {
+ Executor baseExecutor, DiscoveryListener baseListener) {
mServiceType = serviceType;
mProtocolType = protocolType;
+ mBaseExecutor = baseExecutor;
mBaseListener = baseListener;
}
@@ -644,9 +652,11 @@ public final class NsdManager {
final int key = message.arg2;
final Object listener;
final NsdServiceInfo ns;
+ final Executor executor;
synchronized (mMapLock) {
listener = mListenerMap.get(key);
ns = mServiceMap.get(key);
+ executor = mExecutorMap.get(key);
}
if (listener == null) {
Log.d(TAG, "Stale key " + message.arg2);
@@ -657,56 +667,64 @@ public final class NsdManager {
}
switch (what) {
case DISCOVER_SERVICES_STARTED:
- String s = getNsdServiceInfoType((NsdServiceInfo) message.obj);
- ((DiscoveryListener) listener).onDiscoveryStarted(s);
+ final String s = getNsdServiceInfoType((NsdServiceInfo) message.obj);
+ executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStarted(s));
break;
case DISCOVER_SERVICES_FAILED:
removeListener(key);
- ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns),
- message.arg1);
+ executor.execute(() -> ((DiscoveryListener) listener).onStartDiscoveryFailed(
+ getNsdServiceInfoType(ns), message.arg1));
break;
case SERVICE_FOUND:
- ((DiscoveryListener) listener).onServiceFound((NsdServiceInfo) message.obj);
+ executor.execute(() -> ((DiscoveryListener) listener).onServiceFound(
+ (NsdServiceInfo) message.obj));
break;
case SERVICE_LOST:
- ((DiscoveryListener) listener).onServiceLost((NsdServiceInfo) message.obj);
+ executor.execute(() -> ((DiscoveryListener) listener).onServiceLost(
+ (NsdServiceInfo) message.obj));
break;
case STOP_DISCOVERY_FAILED:
// TODO: failure to stop discovery should be internal and retried internally, as
// the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED
removeListener(key);
- ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns),
- message.arg1);
+ executor.execute(() -> ((DiscoveryListener) listener).onStopDiscoveryFailed(
+ getNsdServiceInfoType(ns), message.arg1));
break;
case STOP_DISCOVERY_SUCCEEDED:
removeListener(key);
- ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns));
+ executor.execute(() -> ((DiscoveryListener) listener).onDiscoveryStopped(
+ getNsdServiceInfoType(ns)));
break;
case REGISTER_SERVICE_FAILED:
removeListener(key);
- ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1);
+ executor.execute(() -> ((RegistrationListener) listener).onRegistrationFailed(
+ ns, message.arg1));
break;
case REGISTER_SERVICE_SUCCEEDED:
- ((RegistrationListener) listener).onServiceRegistered(
- (NsdServiceInfo) message.obj);
+ executor.execute(() -> ((RegistrationListener) listener).onServiceRegistered(
+ (NsdServiceInfo) message.obj));
break;
case UNREGISTER_SERVICE_FAILED:
removeListener(key);
- ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1);
+ executor.execute(() -> ((RegistrationListener) listener).onUnregistrationFailed(
+ ns, message.arg1));
break;
case UNREGISTER_SERVICE_SUCCEEDED:
// TODO: do not unregister listener until service is unregistered, or provide
// alternative way for unregistering ?
removeListener(message.arg2);
- ((RegistrationListener) listener).onServiceUnregistered(ns);
+ executor.execute(() -> ((RegistrationListener) listener).onServiceUnregistered(
+ ns));
break;
case RESOLVE_SERVICE_FAILED:
removeListener(key);
- ((ResolveListener) listener).onResolveFailed(ns, message.arg1);
+ executor.execute(() -> ((ResolveListener) listener).onResolveFailed(
+ ns, message.arg1));
break;
case RESOLVE_SERVICE_SUCCEEDED:
removeListener(key);
- ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj);
+ executor.execute(() -> ((ResolveListener) listener).onServiceResolved(
+ (NsdServiceInfo) message.obj));
break;
default:
Log.d(TAG, "Ignored " + message);
@@ -722,7 +740,7 @@ public final class NsdManager {
}
// Assert that the listener is not in the map, then add it and returns its key
- private int putListener(Object listener, NsdServiceInfo s) {
+ private int putListener(Object listener, Executor e, NsdServiceInfo s) {
checkListener(listener);
final int key;
synchronized (mMapLock) {
@@ -733,6 +751,7 @@ public final class NsdManager {
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
+ mExecutorMap.put(key, e);
}
return key;
}
@@ -741,6 +760,7 @@ public final class NsdManager {
synchronized (mMapLock) {
mListenerMap.remove(key);
mServiceMap.remove(key);
+ mExecutorMap.remove(key);
}
}
@@ -779,12 +799,33 @@ public final class NsdManager {
*/
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
+ registerService(serviceInfo, protocolType, Runnable::run, listener);
+ }
+
+ /**
+ * Register a service to be discovered by other services.
+ *
+ * <p> The function call immediately returns after sending a request to register service
+ * to the framework. The application is notified of a successful registration
+ * through the callback {@link RegistrationListener#onServiceRegistered} or a failure
+ * through {@link RegistrationListener#onRegistrationFailed}.
+ *
+ * <p> The application should call {@link #unregisterService} when the service
+ * registration is no longer required, and/or whenever the application is stopped.
+ * @param serviceInfo The service being registered
+ * @param protocolType The service discovery protocol
+ * @param executor Executor to run listener callbacks with
+ * @param listener The listener notifies of a successful registration and is used to
+ * unregister this service through a call on {@link #unregisterService}. Cannot be null.
+ */
+ public void registerService(@NonNull NsdServiceInfo serviceInfo, int protocolType,
+ @NonNull Executor executor, @NonNull RegistrationListener listener) {
if (serviceInfo.getPort() <= 0) {
throw new IllegalArgumentException("Invalid port number");
}
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
- int key = putListener(listener, serviceInfo);
+ int key = putListener(listener, executor, serviceInfo);
try {
mService.registerService(key, serviceInfo);
} catch (RemoteException e) {
@@ -815,11 +856,35 @@ public final class NsdManager {
}
/**
- * Same as {@link #discoverServices(String, int, Network, DiscoveryListener)} with a null
- * {@link Network}.
+ * Initiate service discovery to browse for instances of a service type. Service discovery
+ * consumes network bandwidth and will continue until the application calls
+ * {@link #stopServiceDiscovery}.
+ *
+ * <p> The function call immediately returns after sending a request to start service
+ * discovery to the framework. The application is notified of a success to initiate
+ * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
+ * through {@link DiscoveryListener#onStartDiscoveryFailed}.
+ *
+ * <p> Upon successful start, application is notified when a service is found with
+ * {@link DiscoveryListener#onServiceFound} or when a service is lost with
+ * {@link DiscoveryListener#onServiceLost}.
+ *
+ * <p> Upon failure to start, service discovery is not active and application does
+ * not need to invoke {@link #stopServiceDiscovery}
+ *
+ * <p> The application should call {@link #stopServiceDiscovery} when discovery of this
+ * service type is no longer required, and/or whenever the application is paused or
+ * stopped.
+ *
+ * @param serviceType The service type being discovered. Examples include "_http._tcp" for
+ * http services or "_ipp._tcp" for printers
+ * @param protocolType The service discovery protocol
+ * @param listener The listener notifies of a successful discovery and is used
+ * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
+ * Cannot be null. Cannot be in use for an active service discovery.
*/
public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
- discoverServices(serviceType, protocolType, (Network) null, listener);
+ discoverServices(serviceType, protocolType, (Network) null, Runnable::run, listener);
}
/**
@@ -842,17 +907,17 @@ public final class NsdManager {
* <p> The application should call {@link #stopServiceDiscovery} when discovery of this
* service type is no longer required, and/or whenever the application is paused or
* stopped.
- *
* @param serviceType The service type being discovered. Examples include "_http._tcp" for
* http services or "_ipp._tcp" for printers
* @param protocolType The service discovery protocol
* @param network Network to discover services on, or null to discover on all available networks
+ * @param executor Executor to run listener callbacks with
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
- * Cannot be null. Cannot be in use for an active service discovery.
*/
public void discoverServices(@NonNull String serviceType, int protocolType,
- @Nullable Network network, @NonNull DiscoveryListener listener) {
+ @Nullable Network network, @NonNull Executor executor,
+ @NonNull DiscoveryListener listener) {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
@@ -862,7 +927,7 @@ public final class NsdManager {
s.setServiceType(serviceType);
s.setNetwork(network);
- int key = putListener(listener, s);
+ int key = putListener(listener, executor, s);
try {
mService.discoverServices(key, s);
} catch (RemoteException e) {
@@ -899,18 +964,18 @@ public final class NsdManager {
* themselves are encouraged to use this method instead of other overloads of
* {@code discoverServices}, as they will receive proper notifications when a service becomes
* available or unavailable due to network changes.
- *
* @param serviceType The service type being discovered. Examples include "_http._tcp" for
* http services or "_ipp._tcp" for printers
* @param protocolType The service discovery protocol
* @param networkRequest Request specifying networks that should be considered when discovering
+ * @param executor Executor to run listener callbacks with
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
- * Cannot be null. Cannot be in use for an active service discovery.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void discoverServices(@NonNull String serviceType, int protocolType,
- @NonNull NetworkRequest networkRequest, @NonNull DiscoveryListener listener) {
+ @NonNull NetworkRequest networkRequest, @NonNull Executor executor,
+ @NonNull DiscoveryListener listener) {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
@@ -920,10 +985,10 @@ public final class NsdManager {
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType);
- final int baseListenerKey = putListener(listener, s);
+ final int baseListenerKey = putListener(listener, executor, s);
final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker(
- serviceType, protocolType, listener);
+ serviceType, protocolType, executor, listener);
synchronized (mPerNetworkDiscoveryMap) {
mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo);
@@ -974,8 +1039,21 @@ public final class NsdManager {
* Cannot be in use for an active service resolution.
*/
public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
+ resolveService(serviceInfo, Runnable::run, listener);
+ }
+
+ /**
+ * Resolve a discovered service. An application can resolve a service right before
+ * establishing a connection to fetch the IP and port details on which to setup
+ * the connection.
+ * @param serviceInfo service to be resolved
+ * @param executor Executor to run listener callbacks with
+ * @param listener to receive callback upon success or failure.
+ */
+ public void resolveService(@NonNull NsdServiceInfo serviceInfo,
+ @NonNull Executor executor, @NonNull ResolveListener listener) {
checkServiceInfo(serviceInfo);
- int key = putListener(listener, serviceInfo);
+ int key = putListener(listener, executor, serviceInfo);
try {
mService.resolveService(key, serviceInfo);
} catch (RemoteException e) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index b439f8421b73..998aeeab4b47 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -23,9 +23,11 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
import android.os.image.DynamicSystemManager;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;
+import android.util.Range;
import android.webkit.URLUtil;
import org.json.JSONException;
@@ -48,7 +50,12 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
private static final String TAG = "InstallationAsyncTask";
- private static final int READ_BUFFER_SIZE = 1 << 13;
+ private static final int MIN_SHARED_MEMORY_SIZE = 8 << 10; // 8KiB
+ private static final int MAX_SHARED_MEMORY_SIZE = 1024 << 10; // 1MiB
+ private static final int DEFAULT_SHARED_MEMORY_SIZE = 64 << 10; // 64KiB
+ private static final String SHARED_MEMORY_SIZE_PROP =
+ "dynamic_system.data_transfer.shared_memory.size";
+
private static final long MIN_PROGRESS_TO_PUBLISH = 1 << 27;
private static final List<String> UNSUPPORTED_PARTITIONS =
@@ -131,6 +138,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
void onResult(int resultCode, Throwable detail);
}
+ private final int mSharedMemorySize;
private final String mUrl;
private final String mDsuSlot;
private final String mPublicKey;
@@ -164,6 +172,11 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
Context context,
DynamicSystemManager dynSystem,
ProgressListener listener) {
+ mSharedMemorySize =
+ Range.create(MIN_SHARED_MEMORY_SIZE, MAX_SHARED_MEMORY_SIZE)
+ .clamp(
+ SystemProperties.getInt(
+ SHARED_MEMORY_SIZE_PROP, DEFAULT_SHARED_MEMORY_SIZE));
mUrl = url;
mDsuSlot = dsuSlot;
mPublicKey = publicKey;
@@ -541,10 +554,10 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
Log.d(TAG, "Start installing: " + partitionName);
- MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, READ_BUFFER_SIZE);
+ MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, mSharedMemorySize);
ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor());
- mInstallationSession.setAshmem(pfd, READ_BUFFER_SIZE);
+ mInstallationSession.setAshmem(pfd, memoryFile.length());
mPartitionName = partitionName;
mPartitionSize = partitionSize;
@@ -553,10 +566,10 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
long prevInstalledSize = 0;
long installedSize = 0;
- byte[] bytes = new byte[READ_BUFFER_SIZE];
+ byte[] bytes = new byte[memoryFile.length()];
int numBytesRead;
- while ((numBytesRead = sis.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+ while ((numBytesRead = sis.read(bytes, 0, bytes.length)) != -1) {
if (isCancelled()) {
return;
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
index 4117d0f07e0f..7d2326693a17 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -133,36 +133,32 @@ public class SparseInputStream extends InputStream {
return mLeft == 0;
}
- /**
- * It overrides the InputStream.read(byte[] buf)
- */
- public int read(byte[] buf) throws IOException {
+ @Override
+ public int read(byte[] buf, int off, int len) throws IOException {
if (!mIsSparse) {
- return mIn.read(buf);
+ return mIn.read(buf, off, len);
}
if (prepareChunk()) return -1;
int n = -1;
switch (mCur.mChunkType) {
case SparseChunk.RAW:
- n = mIn.read(buf, 0, (int) min(mLeft, buf.length));
+ n = mIn.read(buf, off, (int) min(mLeft, len));
mLeft -= n;
return n;
case SparseChunk.DONTCARE:
- n = (int) min(mLeft, buf.length);
- Arrays.fill(buf, 0, n - 1, (byte) 0);
+ n = (int) min(mLeft, len);
+ Arrays.fill(buf, off, off + n, (byte) 0);
mLeft -= n;
return n;
case SparseChunk.FILL:
// The FILL type is rarely used, so use a simple implmentation.
- return super.read(buf);
+ return super.read(buf, off, len);
default:
throw new IOException("Unsupported Chunk:" + mCur.toString());
}
}
- /**
- * It overrides the InputStream.read()
- */
+ @Override
public int read() throws IOException {
if (!mIsSparse) {
return mIn.read();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 543a5a0fa772..77d65834da37 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -63,8 +63,7 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
return;
}
- mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback());
- View view = mToolbardelegate.onCreateView(getLayoutInflater(), null);
+ View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null);
super.setContentView(view);
}
@@ -107,7 +106,7 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
@Override
public void setTitle(CharSequence title) {
- mToolbardelegate.setTitle(title);
+ getToolbarDelegate().setTitle(title);
}
@Override
@@ -128,7 +127,7 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
*/
@Nullable
public CollapsingToolbarLayout getCollapsingToolbarLayout() {
- return mToolbardelegate.getCollapsingToolbarLayout();
+ return getToolbarDelegate().getCollapsingToolbarLayout();
}
/**
@@ -136,6 +135,13 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
*/
@Nullable
public AppBarLayout getAppBarLayout() {
- return mToolbardelegate.getAppBarLayout();
+ return getToolbarDelegate().getAppBarLayout();
+ }
+
+ private CollapsingToolbarDelegate getToolbarDelegate() {
+ if (mToolbardelegate == null) {
+ mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback());
+ }
+ return mToolbardelegate;
}
}
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_secondary.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_secondary.xml
new file mode 100644
index 000000000000..98ea3eabe5c1
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_secondary.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="@color/settingslib_text_color_secondary_device_default"/>
+ <item android:color="@color/settingslib_text_color_secondary_device_default"/>
+</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index cba1a9cf0003..87cb41863528 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -24,7 +24,7 @@
<style name="TextAppearance.PreferenceSummary.SettingsLib"
parent="@android:style/TextAppearance.DeviceDefault.Small">
- <item name="android:textColor">@color/settingslib_text_color_secondary_device_default</item>
+ <item name="android:textColor">@color/settingslib_text_color_secondary</item>
</style>
<style name="TextAppearance.CategoryTitle.SettingsLib"
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 3a3ba9e9bdd7..1c1e1ba32f00 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -229,11 +229,11 @@
<string name="bluetooth_active_no_battery_level">Active</string>
<!-- Connected device settings. Message when the left-side hearing aid device is active. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_hearing_aid_left_active">Active, left ear</string>
+ <string name="bluetooth_hearing_aid_left_active">Active, left only</string>
<!-- Connected device settings. Message when the right-side hearing aid device is active. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_hearing_aid_right_active">Active, right ear</string>
+ <string name="bluetooth_hearing_aid_right_active">Active, right only</string>
<!-- Connected device settings. Message when the left-side and right-side hearing aids device are active. [CHAR LIMIT=NONE] -->
- <string name="bluetooth_hearing_aid_left_and_right_active">Active, left and right ears</string>
+ <string name="bluetooth_hearing_aid_left_and_right_active">Active, left and right</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
new file mode 100644
index 000000000000..3ce7a0e4efa5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.bluetooth;
+
+public final class BluetoothBroadcastUtils {
+
+ static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
+
+ // BluetoothLeBroadcastMetadata
+ static final String PREFIX_BT_ADDRESS_TYPE = "T:";
+ static final String PREFIX_BT_DEVICE = "D:";
+ static final String PREFIX_BT_ADVERTISING_SID = "AS:";
+ static final String PREFIX_BT_BROADCAST_ID = "B:";
+ static final String PREFIX_BT_SYNC_INTERVAL = "SI:";
+ static final String PREFIX_BT_IS_ENCRYPTED = "E:";
+ static final String PREFIX_BT_BROADCAST_CODE = "C:";
+ static final String PREFIX_BT_PRESENTATION_DELAY = "D:";
+ static final String PREFIX_BT_SUBGROUPS = "G:";
+ static final String PREFIX_BT_ANDROID_VERSION = "V:";
+
+ // BluetoothLeBroadcastSubgroup
+ static final String PREFIX_BTSG_CODEC_ID = "CID:";
+ static final String PREFIX_BTSG_CODEC_CONFIG = "CC:";
+ static final String PREFIX_BTSG_AUDIO_CONTENT = "AC:";
+ static final String PREFIX_BTSG_CHANNEL_PREF = "CP:";
+ static final String PREFIX_BTSG_BROADCAST_CHANNEL = "BC:";
+
+ // BluetoothLeAudioCodecConfigMetadata
+ static final String PREFIX_BTCC_AUDIO_LOCATION = "AL:";
+ static final String PREFIX_BTCC_RAW_METADATA = "CCRM:";
+
+ // BluetoothLeAudioContentMetadata
+ static final String PREFIX_BTAC_PROGRAM_INFO = "PI:";
+ static final String PREFIX_BTAC_LANGUAGE = "L:";
+ static final String PREFIX_BTAC_RAW_METADATA = "ACRM:";
+
+ // BluetoothLeBroadcastChannel
+ static final String PREFIX_BTBC_CHANNEL_INDEX = "CI:";
+ static final String PREFIX_BTBC_CODEC_CONFIG = "BCCM:";
+
+ static final String DELIMITER_QR_CODE = ";";
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeAudioContentMetadata.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeAudioContentMetadata.java
new file mode 100644
index 000000000000..9df0f4df1c87
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeAudioContentMetadata.java
@@ -0,0 +1,43 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+
+public class LocalBluetoothLeAudioContentMetadata {
+
+ private static final String TAG = "LocalBluetoothLeAudioContentMetadata";
+ private final BluetoothLeAudioContentMetadata mContentMetadata;
+ private final String mLanguage;
+ private final byte[] mRawMetadata;
+ private String mProgramInfo;
+
+ LocalBluetoothLeAudioContentMetadata(BluetoothLeAudioContentMetadata contentMetadata) {
+ mContentMetadata = contentMetadata;
+ mProgramInfo = contentMetadata.getProgramInfo();
+ mLanguage = contentMetadata.getLanguage();
+ mRawMetadata = contentMetadata.getRawMetadata();
+ }
+
+ public void setProgramInfo(String programInfo) {
+ mProgramInfo = programInfo;
+ }
+
+ public String getProgramInfo() {
+ return mProgramInfo;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
new file mode 100644
index 000000000000..bb47c5e10711
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -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.settingslib.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * LocalBluetoothLeBroadcast provides an interface between the Settings app
+ * and the functionality of the local {@link BluetoothLeBroadcast}.
+ */
+public class LocalBluetoothLeBroadcast implements BluetoothLeBroadcast.Callback {
+
+ private static final String TAG = "LocalBluetoothLeBroadcast";
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+ private static final boolean DEBUG = BluetoothUtils.D;
+
+ private BluetoothLeBroadcast mBluetoothLeBroadcast;
+ private LocalBluetoothProfileManager mProfileManager;
+ private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
+ private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
+ private BluetoothLeAudioContentMetadata.Builder mBuilder;
+ private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
+ private boolean mIsProfileReady;
+
+ private final ServiceListener mServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
+ if (DEBUG) {
+ Log.d(TAG,"Bluetooth service connected");
+ }
+ mBluetoothLeBroadcast = (BluetoothLeBroadcast) proxy;
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady = true;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
+ if (DEBUG) {
+ Log.d(TAG,"Bluetooth service disconnected");
+ }
+ mIsProfileReady = false;
+ }
+ }
+ };
+
+ LocalBluetoothLeBroadcast(Context context, LocalBluetoothProfileManager profileManager) {
+ mProfileManager = profileManager;
+ BluetoothAdapter.getDefaultAdapter().
+ getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
+ mBuilder = new BluetoothLeAudioContentMetadata.Builder();
+ }
+
+ public void startBroadcast(byte[] broadcastCode, String language,
+ String programInfo) {
+ if (DEBUG) {
+ if (mBluetoothLeBroadcast == null) {
+ Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast.");
+ return;
+ }
+ Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
+ }
+ buildContentMetadata(language, programInfo);
+ mBluetoothLeBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, broadcastCode);
+ }
+
+ public void stopBroadcast() {
+ if (DEBUG) {
+ if (mBluetoothLeBroadcast == null) {
+ Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
+ return;
+ }
+ Log.d(TAG, "stopBroadcast()");
+ }
+ mBluetoothLeBroadcast.stopBroadcast(mBroadcastId);
+ }
+
+ public void updateBroadcast(String language, String programInfo) {
+ if (DEBUG) {
+ if (mBluetoothLeBroadcast == null) {
+ Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast.");
+ return;
+ }
+ Log.d(TAG,
+ "updateBroadcast: language = " + language + " ,programInfo = " + programInfo);
+ }
+ mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
+ mBluetoothLeBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
+ }
+
+ private void buildContentMetadata(String language, String programInfo) {
+ mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo(
+ programInfo).build();
+ }
+
+ public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() {
+ return new LocalBluetoothLeBroadcastMetadata(mBluetoothLeBroadcastMetadata);
+ }
+
+ @Override
+ public void onBroadcastStarted(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + broadcastId);
+ }
+ }
+
+ @Override
+ public void onBroadcastStartFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onBroadcastMetadataChanged(int broadcastId,
+ @NonNull BluetoothLeBroadcastMetadata metadata) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
+ }
+ mBluetoothLeBroadcastMetadata = metadata;
+ }
+
+ @Override
+ public void onBroadcastStopped(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + broadcastId);
+ }
+ }
+
+ @Override
+ public void onBroadcastStopFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onBroadcastUpdated(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + broadcastId);
+ }
+ }
+
+ @Override
+ public void onBroadcastUpdateFailed(int reason, int broadcastId) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = "
+ + broadcastId);
+ }
+ }
+
+ @Override
+ public void onPlaybackStarted(int reason, int broadcastId) {
+ }
+
+ @Override
+ public void onPlaybackStopped(int reason, int broadcastId) {
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
new file mode 100644
index 000000000000..d904265efda9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java
@@ -0,0 +1,214 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app
+ * and the functionality of the local {@link BluetoothLeBroadcastAssistant}.
+ */
+public class LocalBluetoothLeBroadcastAssistant implements
+ BluetoothLeBroadcastAssistant.Callback {
+
+ private static final String TAG = "LocalBluetoothLeBroadcastAssistant";
+ private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+ private static final boolean DEBUG = BluetoothUtils.D;
+
+ private LocalBluetoothProfileManager mProfileManager;
+ private BluetoothLeBroadcastAssistant mBluetoothLeBroadcastAssistant;
+ private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
+ private BluetoothLeBroadcastMetadata.Builder mBuilder;
+ private boolean mIsProfileReady;
+
+ private final ServiceListener mServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
+ if (DEBUG) {
+ Log.d(TAG,"Bluetooth service connected");
+ }
+ mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady = true;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.LE_AUDIO_BROADCAST) {
+ if (DEBUG) {
+ Log.d(TAG,"Bluetooth service disconnected");
+ }
+ mIsProfileReady = false;
+ }
+ }
+ };
+
+ LocalBluetoothLeBroadcastAssistant(Context context,
+ LocalBluetoothProfileManager profileManager) {
+ mProfileManager = profileManager;
+ BluetoothAdapter.getDefaultAdapter().
+ getProfileProxy(context, mServiceListener,
+ BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ mBuilder = new BluetoothLeBroadcastMetadata.Builder();
+ }
+
+ public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType,
+ int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId,
+ int paSyncInterval, boolean isEncrypted, byte[] broadcastCode,
+ BluetoothDevice sourceDevice, boolean isGroupOp) {
+ if (DEBUG) {
+ Log.d(TAG, "addSource()");
+ }
+ if (mBluetoothLeBroadcastAssistant == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
+ return ;
+ }
+ buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId,
+ paSyncInterval, isEncrypted, broadcastCode, sourceDevice);
+ mBluetoothLeBroadcastAssistant.addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp);
+ }
+
+ private void buildMetadata(int sourceAddressType, int presentationDelayMicros,
+ int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted,
+ byte[] broadcastCode, BluetoothDevice sourceDevice) {
+ mBluetoothLeBroadcastMetadata =
+ mBuilder.setSourceDevice(sourceDevice, sourceAddressType)
+ .setSourceAdvertisingSid(sourceAdvertisingSid)
+ .setBroadcastId(broadcastId)
+ .setPaSyncInterval(paSyncInterval)
+ .setEncrypted(isEncrypted)
+ .setBroadcastCode(broadcastCode)
+ .setPresentationDelayMicros(presentationDelayMicros)
+ .build();
+ }
+
+ public void removeSource(@NonNull BluetoothDevice sink, int sourceId) {
+ if (DEBUG) {
+ Log.d(TAG, "removeSource()");
+ }
+ if (mBluetoothLeBroadcastAssistant == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
+ return ;
+ }
+ mBluetoothLeBroadcastAssistant.removeSource(sink, sourceId);
+ }
+
+ public void startSearchingForSources(@NonNull List<android.bluetooth.le.ScanFilter> filters) {
+ if (DEBUG) {
+ Log.d(TAG, "startSearchingForSources()");
+ }
+ if (mBluetoothLeBroadcastAssistant == null) {
+ Log.d(TAG, "The BluetoothLeBroadcastAssistant is null");
+ return ;
+ }
+ mBluetoothLeBroadcastAssistant.startSearchingForSources(filters);
+ }
+
+ @Override
+ public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceAdded(), reason = " + reason + " , sourceId = " + sourceId);
+ }
+
+ }
+
+ @Override
+ public void onSourceAddFailed(@NonNull BluetoothDevice sink,
+ @NonNull BluetoothLeBroadcastMetadata source, int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceAddFailed(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceRemoved(), reason = " + reason + " , sourceId = " + sourceId);
+ }
+ }
+
+ @Override
+ public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSourceRemoveFailed(), reason = " + reason + " , sourceId = " + sourceId);
+ }
+ }
+
+ @Override
+ public void onSearchStarted(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStarted(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onSearchStartFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStartFailed(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onSearchStopped(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStopped(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onSearchStopFailed(int reason) {
+ if (DEBUG) {
+ Log.d(TAG, "onSearchStopFailed(), reason = " + reason);
+ }
+ }
+
+ @Override
+ public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
+ }
+
+ @Override
+ public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ }
+
+ @Override
+ public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+ }
+
+ @Override
+ public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
+ @NonNull BluetoothLeBroadcastReceiveState state) {
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java
new file mode 100644
index 000000000000..cf4ba8b46c7a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastMetadata.java
@@ -0,0 +1,298 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
+import android.bluetooth.BluetoothLeBroadcastChannel;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeBroadcastSubgroup;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LocalBluetoothLeBroadcastMetadata {
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String TAG = "LocalBluetoothLeBroadcastMetadata";
+ private static final String METADATA_START = "<";
+ private static final String METADATA_END = ">";
+ private static final String PATTERN_REGEX = "<(.*?)>";
+
+ private BluetoothLeBroadcastSubgroup mSubgroup;
+ private List<BluetoothLeBroadcastSubgroup> mSubgroupList;
+
+ // BluetoothLeBroadcastMetadata
+ // Optional: Identity address type
+ private int mSourceAddressType;
+ // Optional: Must use identity address
+ private BluetoothDevice mSourceDevice;
+ private int mSourceAdvertisingSid;
+ private int mBroadcastId;
+ private int mPaSyncInterval;
+ private int mPresentationDelayMicros;
+ private boolean mIsEncrypted;
+ private byte[] mBroadcastCode;
+
+ // BluetoothLeBroadcastSubgroup
+ private long mCodecId;
+ private BluetoothLeAudioContentMetadata mContentMetadata;
+ private BluetoothLeAudioCodecConfigMetadata mConfigMetadata;
+ private BluetoothLeBroadcastChannel mChannel;
+
+ // BluetoothLeAudioCodecConfigMetadata
+ private long mAudioLocation;
+
+ // BluetoothLeAudioContentMetadata
+ private String mLanguage;
+ private String mProgramInfo;
+
+ // BluetoothLeBroadcastChannel
+ private boolean mIsSelected;
+ private int mChannelIndex;
+
+
+ LocalBluetoothLeBroadcastMetadata(BluetoothLeBroadcastMetadata metadata) {
+ mSourceAddressType = metadata.getSourceAddressType();
+ mSourceDevice = metadata.getSourceDevice();
+ mSourceAdvertisingSid = metadata.getSourceAdvertisingSid();
+ mBroadcastId = metadata.getBroadcastId();
+ mPaSyncInterval = metadata.getPaSyncInterval();
+ mIsEncrypted = metadata.isEncrypted();
+ mBroadcastCode = metadata.getBroadcastCode();
+ mPresentationDelayMicros = metadata.getPresentationDelayMicros();
+ mSubgroupList = metadata.getSubgroups();
+ }
+
+ public void setBroadcastCode(byte[] code) {
+ mBroadcastCode = code;
+ }
+
+ public int getBroadcastId() {
+ return mBroadcastId;
+ }
+
+ public String convertToQrCodeString() {
+ return new StringBuilder()
+ .append(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_ADDRESS_TYPE)
+ .append(METADATA_START).append(mSourceAddressType).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_DEVICE)
+ .append(METADATA_START).append(mSourceDevice).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_ADVERTISING_SID)
+ .append(METADATA_START).append(mSourceAdvertisingSid).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_ID)
+ .append(METADATA_START).append(mBroadcastId).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_SYNC_INTERVAL)
+ .append(METADATA_START).append(mPaSyncInterval).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_IS_ENCRYPTED)
+ .append(METADATA_START).append(mIsEncrypted).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_BROADCAST_CODE)
+ .append(METADATA_START).append(Arrays.toString(mBroadcastCode)).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_PRESENTATION_DELAY)
+ .append(METADATA_START).append(mPresentationDelayMicros).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .append(BluetoothBroadcastUtils.PREFIX_BT_SUBGROUPS)
+ .append(METADATA_START).append(mSubgroupList).append(METADATA_END)
+ .append(BluetoothBroadcastUtils.DELIMITER_QR_CODE)
+ .toString();
+ }
+
+ /**
+ * Example : prefix is with the “BT:”, and end by the Android Version.
+ * BT:T:<1>;D:<00:11:22:AA:BB:CC>;AS:<1>;B:…;V:T;;
+ *
+ * @return BluetoothLeBroadcastMetadata
+ */
+ public BluetoothLeBroadcastMetadata convertToBroadcastMetadata(String qrCodeString) {
+ if (DEBUG) {
+ Log.d(TAG, "Convert " + qrCodeString + "to BluetoothLeBroadcastMetadata");
+ }
+ Pattern pattern = Pattern.compile(PATTERN_REGEX);
+ Matcher match = pattern.matcher(qrCodeString);
+ if (match.find()) {
+ ArrayList<String> resultList = new ArrayList<>();
+ resultList.add(match.group(1));
+ mSourceAddressType = Integer.parseInt(resultList.get(0));
+ mSourceDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ resultList.get(1));
+ mSourceAdvertisingSid = Integer.parseInt(resultList.get(2));
+ mBroadcastId = Integer.parseInt(resultList.get(3));
+ mPaSyncInterval = Integer.parseInt(resultList.get(4));
+ mIsEncrypted = Boolean.valueOf(resultList.get(5));
+ mBroadcastCode = resultList.get(6).getBytes();
+ mPresentationDelayMicros = Integer.parseInt(resultList.get(7));
+ mSubgroup = convertToSubgroup(resultList.get(8));
+
+ if (DEBUG) {
+ Log.d(TAG, "Converted qrCodeString result: " + match.group());
+ }
+
+ return new BluetoothLeBroadcastMetadata.Builder()
+ .setSourceDevice(mSourceDevice, mSourceAddressType)
+ .setSourceAdvertisingSid(mSourceAdvertisingSid)
+ .setBroadcastId(mBroadcastId)
+ .setPaSyncInterval(mPaSyncInterval)
+ .setEncrypted(mIsEncrypted)
+ .setBroadcastCode(mBroadcastCode)
+ .setPresentationDelayMicros(mPresentationDelayMicros)
+ .addSubgroup(mSubgroup)
+ .build();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "The match fail, can not convert it to BluetoothLeBroadcastMetadata.");
+ }
+ return null;
+ }
+ }
+
+ private BluetoothLeBroadcastSubgroup convertToSubgroup(String subgroupString) {
+ if (DEBUG) {
+ Log.d(TAG, "Convert " + subgroupString + "to BluetoothLeBroadcastSubgroup");
+ }
+ Pattern pattern = Pattern.compile(PATTERN_REGEX);
+ Matcher match = pattern.matcher(subgroupString);
+ if (match.find()) {
+ ArrayList<String> resultList = new ArrayList<>();
+ resultList.add(match.group(1));
+ mCodecId = Long.getLong(resultList.get(0));
+ mConfigMetadata = convertToConfigMetadata(resultList.get(1));
+ mContentMetadata = convertToContentMetadata(resultList.get(2));
+ mChannel = convertToChannel(resultList.get(3), mConfigMetadata);
+
+ if (DEBUG) {
+ Log.d(TAG, "Converted subgroupString result: " + match.group());
+ }
+
+ return new BluetoothLeBroadcastSubgroup.Builder()
+ .setCodecId(mCodecId)
+ .setCodecSpecificConfig(mConfigMetadata)
+ .setContentMetadata(mContentMetadata)
+ .addChannel(mChannel)
+ .build();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "The match fail, can not convert it to BluetoothLeBroadcastSubgroup.");
+ }
+ return null;
+ }
+ }
+
+ private BluetoothLeAudioCodecConfigMetadata convertToConfigMetadata(
+ String configMetadataString) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Convert " + configMetadataString + "to BluetoothLeAudioCodecConfigMetadata");
+ }
+ Pattern pattern = Pattern.compile(PATTERN_REGEX);
+ Matcher match = pattern.matcher(configMetadataString);
+ if (match.find()) {
+ ArrayList<String> resultList = new ArrayList<>();
+ resultList.add(match.group(1));
+ mAudioLocation = Long.getLong(resultList.get(0));
+
+ if (DEBUG) {
+ Log.d(TAG, "Converted configMetadataString result: " + match.group());
+ }
+
+ return new BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(mAudioLocation)
+ .build();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "The match fail, can not convert it to "
+ + "BluetoothLeAudioCodecConfigMetadata.");
+ }
+ return null;
+ }
+ }
+
+ private BluetoothLeAudioContentMetadata convertToContentMetadata(String contentMetadataString) {
+ if (DEBUG) {
+ Log.d(TAG, "Convert " + contentMetadataString + "to BluetoothLeAudioContentMetadata");
+ }
+ Pattern pattern = Pattern.compile(PATTERN_REGEX);
+ Matcher match = pattern.matcher(contentMetadataString);
+ if (match.find()) {
+ ArrayList<String> resultList = new ArrayList<>();
+ resultList.add(match.group(1));
+ mProgramInfo = resultList.get(0);
+ mLanguage = resultList.get(1);
+
+ if (DEBUG) {
+ Log.d(TAG, "Converted contentMetadataString result: " + match.group());
+ }
+
+ return new BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo(mProgramInfo)
+ .setLanguage(mLanguage)
+ .build();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "The match fail, can not convert it to BluetoothLeAudioContentMetadata.");
+ }
+ return null;
+ }
+ }
+
+ private BluetoothLeBroadcastChannel convertToChannel(String channelString,
+ BluetoothLeAudioCodecConfigMetadata configMetadata) {
+ if (DEBUG) {
+ Log.d(TAG, "Convert " + channelString + "to BluetoothLeBroadcastChannel");
+ }
+ Pattern pattern = Pattern.compile(PATTERN_REGEX);
+ Matcher match = pattern.matcher(channelString);
+ if (match.find()) {
+ ArrayList<String> resultList = new ArrayList<>();
+ resultList.add(match.group(1));
+ mIsSelected = Boolean.valueOf(resultList.get(0));
+ mChannelIndex = Integer.parseInt(resultList.get(1));
+
+ if (DEBUG) {
+ Log.d(TAG, "Converted channelString result: " + match.group());
+ }
+
+ return new BluetoothLeBroadcastChannel.Builder()
+ .setSelected(mIsSelected)
+ .setChannelIndex(mChannelIndex)
+ .setCodecMetadata(configMetadata)
+ .build();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "The match fail, can not convert it to BluetoothLeBroadcastChannel.");
+ }
+ return null;
+ }
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c7673aa98797..f949f99673d9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -146,6 +146,7 @@ public class SecureSettings {
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
Settings.Secure.UI_NIGHT_MODE,
+ Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE,
Settings.Secure.DARK_THEME_CUSTOM_START_TIME,
Settings.Secure.DARK_THEME_CUSTOM_END_TIME,
Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index cbd71c02baf4..2bdf81912709 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -253,6 +253,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.UI_NIGHT_MODE, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.UI_NIGHT_MODE_CUSTOM_TYPE, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.DARK_THEME_CUSTOM_START_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.DARK_THEME_CUSTOM_END_TIME, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.GLOBAL_ACTIONS_PANEL_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index aa6661b2514c..fd7554f11873 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -618,7 +618,7 @@ final class SettingsState {
return;
}
HistoricalOperation operation = new HistoricalOperation(
- SystemClock.elapsedRealtime(), type,
+ System.currentTimeMillis(), type,
setting != null ? new Setting(setting) : null);
if (mNextHistoricalOpIdx >= mHistoricalOperations.size()) {
mHistoricalOperations.add(operation);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 5593fdfe5f51..c480197d23dc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewBoundAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -29,7 +29,7 @@ import android.view.animation.Interpolator
* A class that allows changes in bounds within a view hierarchy to animate seamlessly between the
* start and end state.
*/
-class ViewBoundAnimator {
+class ViewHierarchyAnimator {
// TODO(b/221418522): make this private once it can't be passed as an arg anymore.
enum class Bound(val label: String, val overrideTag: Int) {
LEFT("left", R.id.tag_override_left) {
diff --git a/packages/SystemUI/docs/media-controls.md b/packages/SystemUI/docs/media-controls.md
index 579f453a3a92..112e216dc8f4 100644
--- a/packages/SystemUI/docs/media-controls.md
+++ b/packages/SystemUI/docs/media-controls.md
@@ -41,7 +41,7 @@ Files under [`systemui/media/`](/packages/SystemUI/src/com/android/systemui/medi
* SeekBarViewModel.kt
* Implements its own `computePosition()` for the seekbar (to avoid continually polling the `PlaybackState`, which involves binder calls)
* Does some touch falsing (ignore flings, require drags to start near the thumb - otherwise users would often accidentally trigger the seekbar when they meant to move the carousel or shade)
- * PlayerViewHolder.kt
+ * MediaViewHolder.kt
* Holds references to the UI elements in the panel
* Animation support:
* MediaHierarchyManager.kt
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 d8b050aa8e2c..46dad02ddb45 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -31,37 +31,134 @@ const val ACCENT1_CHROMA = 48.0f
const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
const val MIN_CHROMA = 5
-internal enum class ChromaStrategy {
- EQ, GTE
+internal interface Hue {
+ fun get(sourceColor: Cam): Double
+
+ /**
+ * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the
+ * hue fall betweens, and use the hue rotation of the lower hue.
+ *
+ * @param sourceHue hue of source color
+ * @param hueAndRotations list of pairs, where the first item in a pair is a hue, and the
+ * second item in the pair is a hue rotation that should be applied
+ */
+ fun getHueRotation(sourceHue: Float, hueAndRotations: List<Pair<Int, Int>>): Double {
+ for (i in 0..hueAndRotations.size) {
+ val previousIndex = if (i == 0) hueAndRotations.size - 1 else i - 1
+ val thisHue = hueAndRotations[i].first
+ val previousHue = hueAndRotations[previousIndex].first
+ if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
+ return ColorScheme.wrapDegreesDouble(sourceHue.toDouble() +
+ hueAndRotations[previousIndex].first)
+ }
+ }
+
+ // If this statement executes, something is wrong, there should have been a rotation
+ // found using the arrays.
+ return sourceHue.toDouble()
+ }
}
-internal enum class HueStrategy {
- SOURCE, ADD, SUBTRACT
+internal class HueSource : Hue {
+ override fun get(sourceColor: Cam): Double {
+ return sourceColor.hue.toDouble()
+ }
}
-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 HueAdd(val amountDegrees: Double) : Hue {
+ override fun get(sourceColor: Cam): Double {
+ return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() + amountDegrees)
+ }
+}
+
+internal class HueSubtract(val amountDegrees: Double) : Hue {
+ override fun get(sourceColor: Cam): Double {
+ return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() - amountDegrees)
+ }
+}
+
+internal class HueVibrantSecondary() : Hue {
+ val hueToRotations = listOf(Pair(24, 15), Pair(53, 15), Pair(91, 15), Pair(123, 15),
+ Pair(141, 15), Pair(172, 15), Pair(198, 15), Pair(234, 18), Pair(272, 18),
+ Pair(302, 18), Pair(329, 30), Pair(354, 15))
+ override fun get(sourceColor: Cam): Double {
+ return getHueRotation(sourceColor.hue, hueToRotations)
+ }
+}
+
+internal class HueVibrantTertiary() : Hue {
+ val hueToRotations = listOf(Pair(24, 30), Pair(53, 30), Pair(91, 15), Pair(123, 30),
+ Pair(141, 27), Pair(172, 27), Pair(198, 30), Pair(234, 35), Pair(272, 30),
+ Pair(302, 30), Pair(329, 60), Pair(354, 30))
+ override fun get(sourceColor: Cam): Double {
+ return getHueRotation(sourceColor.hue, hueToRotations)
+ }
+}
+
+internal class HueExpressiveSecondary() : Hue {
+ val hueToRotations = listOf(Pair(24, 95), Pair(53, 45), Pair(91, 45), Pair(123, 20),
+ Pair(141, 45), Pair(172, 45), Pair(198, 15), Pair(234, 15),
+ Pair(272, 45), Pair(302, 45), Pair(329, 45), Pair(354, 45))
+ override fun get(sourceColor: Cam): Double {
+ return getHueRotation(sourceColor.hue, hueToRotations)
+ }
+}
+
+internal class HueExpressiveTertiary() : Hue {
+ val hueToRotations = listOf(Pair(24, 20), Pair(53, 20), Pair(91, 20), Pair(123, 45),
+ Pair(141, 20), Pair(172, 20), Pair(198, 90), Pair(234, 90), Pair(272, 20),
+ Pair(302, 20), Pair(329, 120), Pair(354, 120))
+ override fun get(sourceColor: Cam): Double {
+ return getHueRotation(sourceColor.hue, hueToRotations)
}
}
-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 interface Chroma {
+ fun get(sourceColor: Cam): Double
+
+ /**
+ * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the
+ * hue fall betweens, and use the hue rotation of the lower hue.
+ *
+ * @param sourceHue hue of source color
+ * @param hueAndChromas list of pairs, where the first item in a pair is a hue, and the
+ * second item in the pair is a chroma that should be applied
+ */
+ fun getSpecifiedChroma(sourceHue: Float, hueAndChromas: List<Pair<Int, Int>>): Double {
+ for (i in 0..hueAndChromas.size) {
+ val previousIndex = if (i == 0) hueAndChromas.size - 1 else i - 1
+ val thisHue = hueAndChromas[i].first
+ val previousHue = hueAndChromas[previousIndex].first
+ if (ColorScheme.angleIsBetween(sourceHue, thisHue, previousHue)) {
+ return hueAndChromas[i].second.toDouble()
+ }
}
+
+ // If this statement executes, something is wrong, there should have been a rotation
+ // found using the arrays.
+ return sourceHue.toDouble()
+ }
+}
+
+internal class ChromaConstant(val chroma: Double) : Chroma {
+ override fun get(sourceColor: Cam): Double {
+ return chroma
+ }
+}
+
+internal class ChromaExpressiveNeutral() : Chroma {
+ val hueToChromas = listOf(Pair(24, 8), Pair(53, 8), Pair(91, 8), Pair(123, 8),
+ Pair(141, 6), Pair(172, 6), Pair(198, 8), Pair(234, 8), Pair(272, 8),
+ Pair(302, 8), Pair(329, 8), Pair(354, 8))
+ override fun get(sourceColor: Cam): Double {
+ return getSpecifiedChroma(sourceColor.hue, hueToChromas)
}
}
-internal class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) {
+internal class TonalSpec(val hue: Hue = HueSource(), val chroma: Chroma) {
fun shades(sourceColor: Cam): List<Int> {
- val hue = hue.get(sourceColor.hue.toDouble())
- val chroma = chroma.get(sourceColor.chroma.toDouble())
+ val hue = hue.get(sourceColor)
+ val chroma = chroma.get(sourceColor)
return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
}
}
@@ -76,46 +173,46 @@ internal class CoreSpec(
enum class Style(internal val coreSpec: CoreSpec) {
SPRITZ(CoreSpec(
- a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 12.0)),
- a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
- a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
- n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)),
- n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0))
+ a1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
+ a2 = TonalSpec(HueSource(), ChromaConstant(8.0)),
+ a3 = TonalSpec(HueSource(), ChromaConstant(16.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(2.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(2.0))
)),
TONAL_SPOT(CoreSpec(
- a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 32.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))
+ a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
+ a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
+ a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(4.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
)),
VIBRANT(CoreSpec(
- a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
- a2 = TonalSpec(Hue(HueStrategy.ADD, 15.0), Chroma(ChromaStrategy.EQ, 24.0)),
- a3 = TonalSpec(Hue(HueStrategy.ADD, 30.0), Chroma(ChromaStrategy.GTE, 32.0)),
- n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 8.0)),
- n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ a1 = TonalSpec(HueSource(), ChromaConstant(48.0)),
+ a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
+ a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
)),
EXPRESSIVE(CoreSpec(
- a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 60.0), Chroma(ChromaStrategy.GTE, 64.0)),
- a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 30.0), Chroma(ChromaStrategy.EQ, 24.0)),
- a3 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
- n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 12.0)),
- n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
+ a2 = TonalSpec(HueExpressiveSecondary(), ChromaConstant(24.0)),
+ a3 = TonalSpec(HueExpressiveTertiary(), ChromaConstant(40.0)),
+ n1 = TonalSpec(HueAdd(15.0), ChromaExpressiveNeutral()),
+ n2 = TonalSpec(HueAdd(15.0), ChromaConstant(12.0))
)),
RAINBOW(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, 0.0)),
- n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0))
+ a1 = TonalSpec(HueSource(), ChromaConstant(48.0)),
+ a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
+ a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
)),
FRUIT_SALAD(CoreSpec(
- a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.GTE, 48.0)),
- a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.EQ, 36.0)),
- a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 36.0)),
- n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 10.0)),
- n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ a1 = TonalSpec(HueSubtract(50.0), ChromaConstant(48.0)),
+ a2 = TonalSpec(HueSubtract(50.0), ChromaConstant(36.0)),
+ a3 = TonalSpec(HueSource(), ChromaConstant(36.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
+ n2 = TonalSpec(HueSource(), ChromaConstant(16.0))
)),
}
@@ -296,6 +393,13 @@ class ColorScheme(
return seeds
}
+ internal fun angleIsBetween(angle: Float, a: Int, b: Int): Boolean {
+ if (a < b) {
+ return a <= angle && angle <= b
+ }
+ return a <= angle || angle <= b
+ }
+
private fun wrapDegrees(degrees: Int): Int {
return when {
degrees < 0 -> {
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
index aab3538e3c4c..c97b9601f743 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
@@ -59,9 +59,6 @@ public class Shades {
shades[1] = ColorUtils.CAMToColor(hue, Math.min(40f, chroma), 95);
for (int i = 2; i < 12; i++) {
float lStar = (i == 6) ? MIDDLE_LSTAR : 100 - 10 * (i - 1);
- if (lStar >= 90) {
- chroma = Math.min(40f, chroma);
- }
shades[i] = ColorUtils.CAMToColor(hue, chroma, lStar);
}
return shades;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 6d088f090bcd..1fec3314a13e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -17,6 +17,7 @@ package com.android.systemui.plugins;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
+import android.os.UserHandle;
import android.view.View;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -70,6 +71,9 @@ public interface ActivityStarter {
void startActivity(Intent intent, boolean dismissShade,
@Nullable ActivityLaunchAnimator.Controller animationController,
boolean showOverLockscreenWhenLocked);
+ void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked, UserHandle userHandle);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void postStartActivityDismissingKeyguard(Intent intent, int delay);
diff --git a/packages/SystemUI/res/drawable/new_fgs_dot.xml b/packages/SystemUI/res/drawable/new_fgs_dot.xml
index 759ddaf1e73f..3669e1d3c374 100644
--- a/packages/SystemUI/res/drawable/new_fgs_dot.xml
+++ b/packages/SystemUI/res/drawable/new_fgs_dot.xml
@@ -15,8 +15,9 @@
** limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="oval"
android:width="12dp"
android:height="12dp">
- <solid android:color="@*android:color/red" />
+ <solid android:color="?androidprv:attr/colorAccentTertiary" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_button_background.xml b/packages/SystemUI/res/drawable/qs_media_outline_button.xml
index ed9bd263a79d..ed9bd263a79d 100644
--- a/packages/SystemUI/res/drawable/qs_media_button_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_outline_button.xml
diff --git a/packages/SystemUI/res/drawable/qs_media_solid_button.xml b/packages/SystemUI/res/drawable/qs_media_solid_button.xml
new file mode 100644
index 000000000000..baa4aaee7031
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_solid_button.xml
@@ -0,0 +1,27 @@
+<?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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimaryVariant" />
+ <corners android:radius="24dp"/>
+ <padding
+ android:left="16dp"
+ android:right="16dp"
+ android:top="8dp"
+ android:bottom="8dp" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index 20747fad021b..d39b0d53c743 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -33,7 +33,7 @@
android:layout_height="match_parent"
android:background="@drawable/media_output_item_background"
android:layout_gravity="center_vertical|start">
- <SeekBar
+ <com.android.systemui.media.dialog.MediaOutputSeekbar
android:id="@+id/volume_seekbar"
android:splitTrack="false"
android:visibility="gone"
@@ -127,6 +127,7 @@
android:layout_gravity="right|center"
android:button="@drawable/ic_circle_check_box"
android:visibility="gone"
+ android:clickable="false"
/>
</FrameLayout>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index f030f3130be4..6cf32151d8ea 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -264,7 +264,7 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="0dp"
android:layout_marginStart="@dimen/qs_media_padding"
android:layout_marginEnd="@dimen/qs_media_padding"
android:id="@+id/remove_text"
@@ -274,64 +274,56 @@
android:marqueeRepeatLimit="marquee_forever"
android:text="@string/controls_media_close_session"
android:gravity="center_horizontal|top"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/settings"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/cancel" />
- <FrameLayout
+ <ImageButton
android:id="@+id/settings"
- android:background="@drawable/qs_media_light_source"
+ android:src="@drawable/ic_settings"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="4dp"
+ android:background="@drawable/qs_media_light_source"
+ android:contentDescription="@string/controls_media_settings_button"
+ android:layout_gravity="top"
app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
- <TextView
- android:id="@+id/settings_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_settings_button" />
- </FrameLayout>
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ </ImageButton>
<FrameLayout
- android:id="@+id/cancel"
+ android:id="@+id/dismiss"
android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginStart="@dimen/qs_media_padding"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintStart_toEndOf="@id/settings"
- app:layout_constraintEnd_toStartOf="@id/dismiss"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
- android:id="@+id/cancel_text"
+ android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
+ android:layout_gravity="center|top"
+ style="@style/MediaPlayer.SolidButton"
+ android:background="@drawable/qs_media_solid_button"
+ android:text="@string/controls_media_dismiss_button" />
</FrameLayout>
-
<FrameLayout
- android:id="@+id/dismiss"
+ android:id="@+id/cancel"
android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_padding"
@@ -339,16 +331,16 @@
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
app:layout_constraintHeight_min="@dimen/min_clickable_item_size"
- app:layout_constraintStart_toEndOf="@id/cancel"
+ app:layout_constraintStart_toEndOf="@id/dismiss"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
- android:id="@+id/dismiss_text"
+ android:id="@+id/cancel_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
+ android:layout_gravity="center|top"
style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_dismiss_button" />
+ android:text="@string/cancel" />
</FrameLayout>
</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
deleted file mode 100644
index 9471b9f9786c..000000000000
--- a/packages/SystemUI/res/layout/media_view.xml
+++ /dev/null
@@ -1,300 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<!-- Layout for media controls inside QSPanel carousel -->
-<com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/qs_media_controls"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:gravity="center_horizontal|fill_vertical"
- android:forceHasOverlappingRendering="false"
- android:background="@drawable/qs_media_background"
- android:theme="@style/MediaPlayer">
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/center_vertical_guideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_percent="0.6" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/center_horizontal_guideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_begin="48dp" />
-
- <!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
- <FrameLayout
- android:id="@+id/notification_media_progress_time"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:forceHasOverlappingRendering="false">
- <!-- width is set to "match_parent" to avoid extra layout calls -->
- <TextView
- android:id="@+id/media_elapsed_time"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:gravity="start"
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/media_total_time"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:gravity="end"
- android:textSize="12sp" />
- </FrameLayout>
-
- <!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain
- with the artist name, and must as a group appear at the end of that chain. This is
- accomplished by having all actions appear in a LTR chain within the parent, and then biasing it
- to the right side, then this barrier is used to bound the text views. -->
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/media_action_barrier"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:orientation="vertical"
- app:layout_constraintTop_toBottomOf="@id/header_title"
- app:barrierDirection="start"
- app:constraint_referenced_ids="action0,action1,action2,action3,action4"
- />
-
- <ImageButton
- android:id="@+id/action0"
- style="@style/MediaPlayer.Action"
- android:layout_width="48dp"
- android:layout_height="48dp" />
-
- <ImageButton
- android:id="@+id/action1"
- style="@style/MediaPlayer.Action"
- android:layout_width="48dp"
- android:layout_height="48dp" />
-
- <ImageButton
- android:id="@+id/action2"
- style="@style/MediaPlayer.Action"
- android:layout_width="48dp"
- android:layout_height="48dp" />
-
- <ImageButton
- android:id="@+id/action3"
- style="@style/MediaPlayer.Action"
- android:layout_width="48dp"
- android:layout_height="48dp" />
-
- <ImageButton
- android:id="@+id/action4"
- style="@style/MediaPlayer.Action"
- android:layout_width="48dp"
- android:layout_height="48dp" />
-
- <!-- Album Art -->
- <ImageView
- android:id="@+id/album_art"
- android:layout_width="@dimen/qs_media_album_size"
- android:layout_height="@dimen/qs_media_album_size"
- android:layout_gravity="center_vertical"
- style="@style/MediaPlayer.Album"
- android:background="@drawable/qs_media_art_background"
- android:clipToOutline="true" />
-
- <!-- Seamless Output Switcher -->
- <LinearLayout
- android:id="@+id/media_seamless"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:orientation="horizontal"
- android:gravity="top|end"
- android:paddingTop="@dimen/qs_media_padding"
- android:paddingEnd="@dimen/qs_media_padding"
- android:background="@drawable/qs_media_light_source"
- android:forceHasOverlappingRendering="false">
- <LinearLayout
- android:id="@+id/media_seamless_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/qs_seamless_height"
- android:theme="@style/MediaPlayer.SolidButton"
- android:background="@drawable/qs_media_seamless_background"
- android:orientation="horizontal"
- android:contentDescription="@string/quick_settings_media_device_label">
- <ImageView
- android:id="@+id/media_seamless_image"
- android:layout_width="@dimen/qs_seamless_icon_size"
- android:layout_height="@dimen/qs_seamless_icon_size"
- android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@*android:drawable/ic_media_seamless" />
- <TextView
- android:id="@+id/media_seamless_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="4dp"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:singleLine="true"
- android:text="@*android:string/ext_media_seamless_action"
- android:textDirection="locale"
- android:textSize="12sp"
- android:lineHeight="16sp" />
- </LinearLayout>
- </LinearLayout>
-
- <!-- Seek Bar -->
- <!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
- <SeekBar
- android:id="@+id/media_progress_bar"
- style="@style/MediaPlayer.ProgressBar"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
- android:paddingTop="@dimen/qs_media_enabled_seekbar_vertical_padding"
- android:layout_marginTop="-22dp"
- android:paddingBottom="2dp"
- android:splitTrack="false" />
-
- <!-- Song name -->
- <TextView
- android:id="@+id/header_title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:singleLine="true"
- android:textSize="16sp" />
-
- <!-- Artist name -->
- <TextView
- android:id="@+id/header_artist"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- style="@style/MediaPlayer.Subtitle"
- android:textSize="14sp" />
-
- <com.android.internal.widget.CachingIconView
- android:id="@+id/icon"
- style="@style/MediaPlayer.AppIcon"
- android:layout_width="@dimen/qs_media_icon_size"
- android:layout_height="@dimen/qs_media_icon_size" />
-
- <!-- Long press menu -->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:id="@+id/remove_text"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="marquee_forever"
- android:text="@string/controls_media_close_session"
- android:gravity="center_horizontal|top"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/cancel"/>
-
- <FrameLayout
- android:id="@+id/settings"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/cancel"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:id="@+id/settings_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_settings_button" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/cancel"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/settings"
- app:layout_constraintEnd_toStartOf="@id/dismiss"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:id="@+id/cancel_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/cancel" />
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/dismiss"
- android:background="@drawable/qs_media_light_source"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- app:layout_constraintStart_toEndOf="@id/cancel"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/remove_text">
-
- <TextView
- android:id="@+id/dismiss_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center|bottom"
- style="@style/MediaPlayer.OutlineButton"
- android:text="@string/controls_media_dismiss_button"
- />
- </FrameLayout>
-</com.android.systemui.util.animation.TransitionLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
deleted file mode 100644
index bb6d4bddf25a..000000000000
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-<com.android.systemui.RegionInterceptingFrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/rounded_corners_bottom"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/left"
- android:layout_width="12dp"
- android:layout_height="12dp"
- android:layout_gravity="left|bottom"
- android:tint="#ff000000"
- android:visibility="gone"
- android:src="@drawable/rounded_corner_bottom"/>
-
- <ImageView
- android:id="@+id/right"
- android:layout_width="12dp"
- android:layout_height="12dp"
- android:tint="#ff000000"
- android:visibility="gone"
- android:layout_gravity="right|bottom"
- android:src="@drawable/rounded_corner_bottom"/>
-
-</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
deleted file mode 100644
index 46648c88d921..000000000000
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-<com.android.systemui.RegionInterceptingFrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/rounded_corners_top"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/left"
- android:layout_width="12dp"
- android:layout_height="12dp"
- android:layout_gravity="left|top"
- android:tint="#ff000000"
- android:visibility="gone"
- android:src="@drawable/rounded_corner_top"/>
-
- <ImageView
- android:id="@+id/right"
- android:layout_width="12dp"
- android:layout_height="12dp"
- android:tint="#ff000000"
- android:visibility="gone"
- android:layout_gravity="right|top"
- android:src="@drawable/rounded_corner_top"/>
-
-</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8f4e11527d95..73457262fd17 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -959,10 +959,7 @@
<!-- Size of media cards in the QSPanel carousel -->
<dimen name="qs_media_padding">16dp</dimen>
- <dimen name="qs_media_album_size_small">72dp</dimen>
- <dimen name="qs_media_album_size">84dp</dimen>
<dimen name="qs_media_album_radius">14dp</dimen>
- <dimen name="qs_media_album_device_padding">26dp</dimen>
<dimen name="qs_media_info_margin">12dp</dimen>
<dimen name="qs_media_info_spacing">8dp</dimen>
<dimen name="qs_media_icon_size">20dp</dimen>
@@ -974,10 +971,7 @@
<dimen name="qs_seamless_icon_size">12dp</dimen>
<dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
<dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
- <dimen name="qs_media_enabled_seekbar_vertical_padding">28dp</dimen>
- <dimen name="qs_media_disabled_seekbar_vertical_padding">29dp</dimen>
- <!-- Sizes for alternate session-based layout -->
<dimen name="qs_media_session_enabled_seekbar_vertical_padding">15dp</dimen>
<dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen>
<dimen name="qs_media_session_height_expanded">184dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 926734c2749f..096b9a0c4790 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -167,5 +167,11 @@
<item type="id" name="action_move_bottom_right"/>
<item type="id" name="action_move_to_edge_and_hide"/>
<item type="id" name="action_move_out_edge_and_show"/>
+
+ <!-- rounded corner view id -->
+ <item type="id" name="rounded_corner_top_left"/>
+ <item type="id" name="rounded_corner_top_right"/>
+ <item type="id" name="rounded_corner_bottom_left"/>
+ <item type="id" name="rounded_corner_bottom_right"/>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ee4b9a13058e..d3b76d90d09f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2116,11 +2116,11 @@
<!-- Title for media controls [CHAR_LIMIT=50] -->
<string name="controls_media_title">Media</string>
<!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=50] -->
- <string name="controls_media_close_session">Hide this media session?</string>
+ <string name="controls_media_close_session">Hide this media control for <xliff:g id="app_name" example="YouTube Music">%1$s</xliff:g>?</string>
<!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=50] -->
<string name="controls_media_active_session">The current media session cannot be hidden.</string>
<!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
- <string name="controls_media_dismiss_button">Dismiss</string>
+ <string name="controls_media_dismiss_button">Hide</string>
<!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
<string name="controls_media_resume">Resume</string>
<!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f5c1382b9ae9..a61eda8f0e09 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -630,7 +630,7 @@
</style>
<style name="MediaPlayer.OutlineButton">
- <item name="android:background">@drawable/qs_media_button_background</item>
+ <item name="android:background">@drawable/qs_media_outline_button</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:backgroundTint">@color/media_player_outline_button_bg</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
deleted file mode 100644
index 12e446f53634..000000000000
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ /dev/null
@@ -1,176 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <Constraint
- android:id="@+id/icon"
- android:layout_width="@dimen/qs_media_icon_size"
- android:layout_height="@dimen/qs_media_icon_size"
- android:translationY="@dimen/qs_media_icon_offset"
- android:translationX="@dimen/qs_media_icon_offset"
- app:layout_constraintEnd_toEndOf="@id/album_art"
- app:layout_constraintBottom_toBottomOf="@id/album_art"
- />
-
- <Constraint
- android:id="@+id/media_seamless"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
- app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- android:layout_marginStart="@dimen/qs_center_guideline_padding"
- />
-
- <Constraint
- android:id="@+id/album_art"
- android:layout_width="@dimen/qs_media_album_size_small"
- android:layout_height="@dimen/qs_media_album_size_small"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- />
-
- <!-- Song name -->
- <Constraint
- android:id="@+id/header_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_margin"
- android:layout_marginEnd="@dimen/qs_center_guideline_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
- app:layout_constraintStart_toEndOf="@id/album_art"
- app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
- app:layout_constraintHorizontal_bias="0"/>
-
- <!-- Artist name -->
- <Constraint
- android:id="@+id/header_artist"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constrainedWidth="true"
- android:layout_marginTop="@dimen/qs_media_info_spacing"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintStart_toStartOf="@id/header_title"
- app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
- app:layout_constraintHorizontal_bias="0"/>
-
- <!-- Seek Bar -->
- <Constraint
- android:id="@+id/media_progress_bar"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:alpha="0.0"
- app:layout_constraintTop_toBottomOf="@id/album_art"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- android:visibility="gone"
- />
-
- <Constraint
- android:id="@+id/notification_media_progress_time"
- android:alpha="0.0"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- app:layout_constraintTop_toBottomOf="@id/album_art"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- android:visibility="gone"
- />
-
- <Constraint
- android:id="@+id/action0"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:visibility="gone"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toLeftOf="@id/action1"
- app:layout_constraintHorizontal_bias="1"
- >
- </Constraint>
-
- <Constraint
- android:id="@+id/action1"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toRightOf="@id/action0"
- app:layout_constraintRight_toLeftOf="@id/action2"
- >
- </Constraint>
-
- <Constraint
- android:id="@+id/action2"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toRightOf="@id/action1"
- app:layout_constraintRight_toLeftOf="@id/action3"
- >
- </Constraint>
-
- <Constraint
- android:id="@+id/action3"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toRightOf="@id/action2"
- app:layout_constraintRight_toLeftOf="@id/action4"
- >
- </Constraint>
-
- <Constraint
- android:id="@+id/action4"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:visibility="gone"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toRightOf="@id/action3"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintHorizontal_bias="0"
- >
- </Constraint>
-</ConstraintSet>
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
deleted file mode 100644
index 6b83aae38e15..000000000000
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <Constraint
- android:id="@+id/icon"
- android:layout_width="@dimen/qs_media_icon_size"
- android:layout_height="@dimen/qs_media_icon_size"
- android:translationY="@dimen/qs_media_icon_offset"
- android:translationX="@dimen/qs_media_icon_offset"
- app:layout_constraintEnd_toEndOf="@id/album_art"
- app:layout_constraintBottom_toBottomOf="@id/album_art"
- />
-
- <Constraint
- android:id="@+id/media_seamless"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constrainedWidth="true"
- app:layout_constraintWidth_min="48dp"
- app:layout_constraintHeight_min="48dp"
- android:paddingTop="@dimen/qs_media_padding"
- android:paddingEnd="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_center_guideline_padding"
- android:layout_marginBottom="4dp" />
-
- <Constraint
- android:id="@+id/album_art"
- android:layout_width="@dimen/qs_media_album_size"
- android:layout_height="@dimen/qs_media_album_size"
- android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginBottom="0dp"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- />
-
- <!-- Song name -->
- <Constraint
- android:id="@+id/header_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="26dp"
- android:layout_marginStart="@dimen/qs_media_info_margin"
- android:layout_marginEnd="@dimen/qs_media_padding"
- app:layout_constrainedWidth="true"
- app:layout_constraintTop_toTopOf="@+id/album_art"
- app:layout_constraintStart_toEndOf="@id/album_art"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0"/>
-
- <!-- Artist name -->
- <Constraint
- android:id="@+id/header_artist"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_info_margin"
- app:layout_constrainedWidth="true"
- android:layout_marginTop="1dp"
- app:layout_constraintTop_toBottomOf="@id/header_title"
- app:layout_constraintStart_toStartOf="@id/header_title"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0"/>
-
- <!-- Seek Bar -->
- <Constraint
- android:id="@+id/media_progress_bar"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="34dp"
- app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- />
-
- <Constraint
- android:id="@+id/notification_media_progress_time"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- />
-
- <Constraint
- android:id="@+id/action0"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_action_margin"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toLeftOf="@id/action1"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent">
- </Constraint>
-
- <Constraint
- android:id="@+id/action1"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_action_margin"
- app:layout_constraintLeft_toRightOf="@id/action0"
- app:layout_constraintRight_toLeftOf="@id/action2"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent">
- </Constraint>
-
- <Constraint
- android:id="@+id/action2"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_action_margin"
- app:layout_constraintLeft_toRightOf="@id/action1"
- app:layout_constraintRight_toLeftOf="@id/action3"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent">
- </Constraint>
-
- <Constraint
- android:id="@+id/action3"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_action_margin"
- app:layout_constraintLeft_toRightOf="@id/action2"
- app:layout_constraintRight_toLeftOf="@id/action4"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent">
- </Constraint>
-
- <Constraint
- android:id="@+id/action4"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginStart="@dimen/qs_media_action_spacing"
- android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_action_margin"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintLeft_toRightOf="@id/action3"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent">
- </Constraint>
-</ConstraintSet>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 75579b05aeeb..5ab2fd0aff09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -57,7 +57,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- private boolean mBouncerVisible;
+ private boolean mBouncerShowing;
private boolean mAltBouncerShowing;
/**
* Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
@@ -177,7 +177,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
void update() {
CharSequence status = mMessage;
- setVisibility(TextUtils.isEmpty(status) || (!mBouncerVisible && !mAltBouncerShowing)
+ setVisibility(TextUtils.isEmpty(status) || (!mBouncerShowing && !mAltBouncerShowing)
? INVISIBLE : VISIBLE);
setText(status);
ColorStateList colorState = mDefaultColorState;
@@ -192,8 +192,14 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp
setTextColor(colorState);
}
- public void setBouncerVisible(boolean bouncerVisible) {
- mBouncerVisible = bouncerVisible;
+ /**
+ * Set whether the bouncer is fully showing
+ */
+ public void setBouncerShowing(boolean bouncerShowing) {
+ if (mBouncerShowing != bouncerShowing) {
+ mBouncerShowing = bouncerShowing;
+ update();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 05318bb0df78..81cc3a933600 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -39,12 +39,6 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
public void onStartedWakingUp() {
mView.setSelected(true);
}
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- mView.setBouncerVisible(bouncer);
- mView.update();
- }
};
private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@@ -94,6 +88,13 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
mView.setAltBouncerShowing(showing);
}
+ /**
+ * Set bouncer is fully showing
+ */
+ public void setBouncerShowing(boolean showing) {
+ mView.setBouncerShowing(showing);
+ }
+
public void setMessage(CharSequence s) {
mView.setMessage(s);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3858f9cd7a82..5602f3d3801b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1486,6 +1486,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationFailed() {
handleFingerprintAuthFailed();
+
+ // TODO(b/225231929): Refactor as needed, add tests, etc.
+ mTrustManager.reportUserRequestedUnlock(
+ KeyguardUpdateMonitor.getCurrentUser(), true);
}
@Override
@@ -1497,17 +1501,23 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationHelp");
handleFingerprintHelp(helpMsgId, helpString.toString());
+ Trace.endSection();
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationError");
handleFingerprintError(errMsgId, errString.toString());
+ Trace.endSection();
}
@Override
public void onAuthenticationAcquired(int acquireInfo) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationAcquired");
handleFingerprintAcquired(acquireInfo);
+ Trace.endSection();
}
@Override
@@ -2258,7 +2268,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (shouldTriggerActiveUnlock()) {
- mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser());
+ // TODO(b/225231929): Refactor surrounding code to reflect calling of new method
+ mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index e36e984380e2..a4a0105fae3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -212,19 +212,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
updateBurnInOffsets();
updateVisibility();
+ mAccessibilityManager.addTouchExplorationStateChangeListener(
+ mTouchExplorationStateChangeListener);
updateAccessibility();
}
private void updateAccessibility() {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
- mView.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- onLongPress();
- }
- }
- );
+ mView.setOnClickListener(mA11yClickListener);
} else {
mView.setOnClickListener(null);
}
@@ -242,6 +237,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mCancelDelayedUpdateVisibilityRunnable.run();
mCancelDelayedUpdateVisibilityRunnable = null;
}
+
+ mAccessibilityManager.removeTouchExplorationStateChangeListener(
+ mTouchExplorationStateChangeListener);
}
public float getTop() {
@@ -267,7 +265,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
return;
}
- boolean wasShowingUnlock = mShowUnlockIcon;
boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
&& !mShowAodUnlockedIcon && !mShowAodLockIcon;
mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
@@ -702,6 +699,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mView.setAlpha(alpha);
}
+ private void updateUdfpsConfig() {
+ // must be called from the main thread since it may update the views
+ mExecutor.execute(() -> {
+ updateIsUdfpsEnrolled();
+ updateConfiguration();
+ });
+ }
+
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
public void onAllAuthenticatorsRegistered() {
@@ -714,11 +719,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
};
- private void updateUdfpsConfig() {
- // must be called from the main thread since it may update the views
- mExecutor.execute(() -> {
- updateIsUdfpsEnrolled();
- updateConfiguration();
- });
- }
+ private final View.OnClickListener mA11yClickListener = v -> onLongPress();
+
+ private final AccessibilityManager.TouchExplorationStateChangeListener
+ mTouchExplorationStateChangeListener = enabled -> updateAccessibility();
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 5bd620e873b0..7af6f6677f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -16,6 +16,7 @@ package com.android.systemui;
import android.app.PendingIntent;
import android.content.Intent;
+import android.os.UserHandle;
import android.view.View;
import androidx.annotation.Nullable;
@@ -100,6 +101,15 @@ public class ActivityStarterDelegate implements ActivityStarter {
}
@Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked, UserHandle userHandle) {
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade, animationController,
+ showOverLockscreenWhenLocked, userHandle));
+ }
+
+ @Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
mActualStarterOptionalLazy.get().ifPresent(
starter -> starter.startActivity(intent, onlyProvisioned, dismissShade));
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index 56046d9c057d..ccb5b1146a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -79,6 +79,8 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
updateCutout()
+ updateProtectionBoundingPath()
+ onUpdate()
}
fun onDisplayChanged(displayId: Int) {
@@ -93,6 +95,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
if (displayId == display.displayId) {
updateCutout()
updateProtectionBoundingPath()
+ onUpdate()
}
}
@@ -100,8 +103,13 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView {
displayRotation = rotation
updateCutout()
updateProtectionBoundingPath()
+ onUpdate()
}
+ // Called after the cutout and protection bounding path change. Subclasses
+ // should make any changes that need to happen based on the change.
+ open fun onUpdate() = Unit
+
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index b5b6b1340108..9a6020f8556b 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -128,7 +128,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
UserSwitcherController userSwitcherController,
UiEventLogger uiEventLogger,
int userId) {
- super(context);
+ super(context, false /* dismissOnDeviceLock */);
setTitle(context.getString(R.string.guest_wipe_session_title));
setMessage(context.getString(R.string.guest_wipe_session_message));
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
index 498e71546bed..011881354e35 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -101,10 +101,10 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
override fun onAttachedToWindow() {
super.onAttachedToWindow()
+ parent.requestTransparentRegion(this)
if (!DEBUG_COLOR) {
- parent.requestTransparentRegion(this)
+ viewRootImpl.setDisplayDecoration(true)
}
- viewRootImpl.setDisplayDecoration(true)
if (useInvertedAlphaColor) {
paint.set(clearPaint)
@@ -114,6 +114,10 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
}
}
+ override fun onUpdate() {
+ parent.requestTransparentRegion(this)
+ }
+
override fun onDraw(canvas: Canvas) {
// If updating onDraw, also update gatherTransparentRegion
if (useInvertedAlphaColor) {
@@ -128,7 +132,6 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
super.onDraw(canvas)
debugTransparentRegionPaint?.let {
- calculateTransparentRect()
canvas.drawRect(transparentRect, it)
}
}
@@ -136,7 +139,16 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
override fun gatherTransparentRegion(region: Region?): Boolean {
region?.let {
calculateTransparentRect()
- region.op(transparentRect, Region.Op.INTERSECT)
+ if (DEBUG_COLOR) {
+ // Since we're going to draw a rectangle where the layer would
+ // normally be transparent, treat the transparent region as
+ // empty. We still want this method to be called, though, so
+ // that it calculates the transparent rect at the right time
+ // to match !DEBUG_COLOR.
+ region.setEmpty()
+ } else {
+ region.op(transparentRect, Region.Op.INTERSECT)
+ }
}
// Always return false - views underneath this should always be visible.
return false
@@ -357,10 +369,15 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
* Update the rounded corner size.
*/
fun updateRoundedCornerSize(top: Int, bottom: Int) {
+ if (roundedCornerTopSize == top && roundedCornerBottomSize == bottom) {
+ return
+ }
roundedCornerTopSize = top
roundedCornerBottomSize = bottom
updateRoundedCornerDrawableBounds()
- invalidate()
+
+ // Use requestLayout() to trigger transparent region recalculated
+ requestLayout()
}
private fun updateRoundedCornerDrawableBounds() {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 2f5292cec909..5de09b13bf6a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.DecorProviderKt;
import com.android.systemui.decor.OverlayWindow;
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
+import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegate;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.settings.UserTracker;
@@ -138,6 +139,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
@VisibleForTesting
protected RoundedCornerResDelegate mRoundedCornerResDelegate;
@VisibleForTesting
+ protected DecorProviderFactory mRoundedCornerFactory;
+ private int mProviderRefreshToken = 0;
+ @VisibleForTesting
protected OverlayWindow[] mOverlays = null;
@Nullable
private DisplayCutoutView[] mCutoutViews;
@@ -292,11 +296,11 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
+ mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
- updateRoundedCornerDrawable();
- updateRoundedCornerRadii();
+ updateHwLayerRoundedCornerDrawable();
setupDecorations();
setupCameraListener();
@@ -348,7 +352,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
final String newUniqueId = mContext.getDisplay().getUniqueId();
if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
mDisplayUniqueId = newUniqueId;
- mRoundedCornerResDelegate.reloadAll(newUniqueId);
+ mRoundedCornerResDelegate.updateDisplayUniqueId(newUniqueId, null);
final DisplayDecorationSupport newScreenDecorationSupport =
mContext.getDisplay().getDisplayDecorationSupport();
// When the value of mSupportHwcScreenDecoration is changed, re-setup the whole
@@ -359,12 +363,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
setupDecorations();
return;
}
- updateRoundedCornerDrawable();
+ updateHwLayerRoundedCornerDrawable();
}
if (mScreenDecorHwcLayer != null) {
mScreenDecorHwcLayer.onDisplayChanged(displayId);
}
- updateOrientation();
+ updateView();
}
};
@@ -406,22 +410,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
private void setupDecorations() {
- List<DecorProvider> decorProviders = mDotFactory.getProviders();
-
- if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
+ if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) {
+ List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders());
if (mHwcScreenDecorationSupport != null) {
createHwcOverlay();
} else {
removeHwcOverlay();
+ decorProviders.addAll(mRoundedCornerFactory.getProviders());
}
final DisplayCutout cutout = getCutout();
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
- || shouldShowPrivacyDot(i, cutout)) {
+ if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout)
+ || shouldShowSwLayerPrivacyDot(i, cutout)) {
Pair<List<DecorProvider>, List<DecorProvider>> pair =
DecorProviderKt.partitionAlignedBound(decorProviders, i);
decorProviders = pair.getSecond();
- createOverlay(i, cutout, pair.getFirst());
+ createOverlay(i, pair.getFirst());
} else {
removeOverlay(i);
}
@@ -522,7 +526,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
private void createOverlay(
@BoundsPosition int pos,
- @Nullable DisplayCutout cutout,
@NonNull List<DecorProvider> decorProviders) {
if (mOverlays == null) {
mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
@@ -547,7 +550,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
mCutoutViews[pos].setColor(mTintColor);
overlayView.addView(mCutoutViews[pos]);
- updateView(pos, cutout);
+ mCutoutViews[pos].updateRotation(mRotation);
}
mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
@@ -603,7 +606,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
private OverlayWindow overlayForPosition(
@BoundsPosition int pos,
@NonNull List<DecorProvider> decorProviders) {
- final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos);
+ final OverlayWindow currentOverlay = new OverlayWindow(mContext);
decorProviders.forEach(provider -> {
removeOverlayView(provider.getViewId());
currentOverlay.addDecorProvider(provider, mRotation);
@@ -617,22 +620,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
return currentOverlay;
}
- private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
- if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) {
+ private void updateView() {
+ if (mOverlays == null) {
return;
}
-
- // update rounded corner view rotation
- updateRoundedCornerView(pos, R.id.left, cutout);
- updateRoundedCornerView(pos, R.id.right, cutout);
- updateRoundedCornerSize(
- mRoundedCornerResDelegate.getTopRoundedSize(),
- mRoundedCornerResDelegate.getBottomRoundedSize());
- updateRoundedCornerImageView();
-
- // update cutout view rotation
- if (mCutoutViews != null && mCutoutViews[pos] != null) {
- mCutoutViews[pos].updateRotation(mRotation);
+ ++mProviderRefreshToken;
+ for (final OverlayWindow overlay: mOverlays) {
+ if (overlay == null) {
+ continue;
+ }
+ overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId);
}
}
@@ -806,7 +803,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
int oldRotation = mRotation;
mPendingRotationChange = false;
updateOrientation();
- updateRoundedCornerRadii();
if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
setupDecorations();
if (mOverlays != null) {
@@ -866,109 +862,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
mDotViewController.setNewRotation(newRotation);
}
- if (mPendingRotationChange) {
- return;
- }
- if (newRotation != mRotation) {
+ if (!mPendingRotationChange && newRotation != mRotation) {
mRotation = newRotation;
if (mScreenDecorHwcLayer != null) {
mScreenDecorHwcLayer.pendingRotationChange = false;
mScreenDecorHwcLayer.updateRotation(mRotation);
+ updateHwLayerRoundedCornerSize();
+ updateHwLayerRoundedCornerDrawable();
}
- if (mOverlays != null) {
- updateLayoutParams();
- final DisplayCutout cutout = getCutout();
- for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (mOverlays[i] == null) {
+ updateLayoutParams();
+ // update cutout view rotation
+ if (mCutoutViews != null) {
+ for (final DisplayCutoutView cutoutView: mCutoutViews) {
+ if (cutoutView == null) {
continue;
}
- updateView(i, cutout);
+ cutoutView.updateRotation(mRotation);
}
}
}
- }
-
- private void updateRoundedCornerRadii() {
- // We should eventually move to just using the intrinsic size of the drawables since
- // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
- // upgrading all of the configs to contain (width, height) pairs. Instead assume that a
- // device configured using the single integer config value is okay with drawing the corners
- // as a square
- final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
- final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();
- mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
- final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
- final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();
-
- if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth()
- || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) {
- onTuningChanged(SIZE, null);
- }
- }
-
- private void updateRoundedCornerView(@BoundsPosition int pos, int id,
- @Nullable DisplayCutout cutout) {
- final View rounded = mOverlays[pos].getRootView().findViewById(id);
- if (rounded == null) {
- return;
- }
- rounded.setVisibility(View.GONE);
- if (shouldShowRoundedCorner(pos, cutout)) {
- final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
- ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
- setRoundedCornerOrientation(rounded, gravity);
- rounded.setVisibility(View.VISIBLE);
- }
- }
- private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
- final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
- switch (rotatedPos) {
- case BOUNDS_POSITION_LEFT:
- return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT;
- case BOUNDS_POSITION_TOP:
- return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT;
- case BOUNDS_POSITION_RIGHT:
- return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT;
- case BOUNDS_POSITION_BOTTOM:
- return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT;
- default:
- throw new IllegalArgumentException("Incorrect position: " + rotatedPos);
- }
+ // update views
+ updateView();
}
- /**
- * Configures the rounded corner drawable's view matrix based on the gravity.
- *
- * The gravity describes which corner to configure for, and the drawable we are rotating is
- * assumed to be oriented for the top-left corner of the device regardless of the target corner.
- * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
- * y-axis for the top-right and bottom-left corners.
- */
- private void setRoundedCornerOrientation(View corner, int gravity) {
- corner.setRotation(0);
- corner.setScaleX(1);
- corner.setScaleY(1);
- switch (gravity) {
- case Gravity.TOP | Gravity.LEFT:
- return;
- case Gravity.TOP | Gravity.RIGHT:
- corner.setScaleX(-1); // flip X axis
- return;
- case Gravity.BOTTOM | Gravity.LEFT:
- corner.setScaleY(-1); // flip Y axis
- return;
- case Gravity.BOTTOM | Gravity.RIGHT:
- corner.setRotation(180);
- return;
- default:
- throw new IllegalArgumentException("Unsupported gravity: " + gravity);
- }
- }
private boolean hasRoundedCorners() {
- return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0
- || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0
- || mRoundedCornerResDelegate.isMultipleRadius();
+ return mRoundedCornerFactory.getHasProviders();
}
private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
@@ -987,17 +906,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
}
- private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
+ private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos,
@Nullable DisplayCutout cutout) {
return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
&& mHwcScreenDecorationSupport == null;
}
- private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+ private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos,
+ @Nullable DisplayCutout cutout) {
return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
}
- private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+ private boolean shouldShowSwLayerCutout(@BoundsPosition int pos,
+ @Nullable DisplayCutout cutout) {
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
return (bounds != null && !bounds[rotatedPos].isEmpty()
@@ -1032,54 +953,33 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
return;
}
mExecutor.execute(() -> {
- if (mOverlays == null) return;
- if (SIZE.equals(key)) {
- if (newValue != null) {
- try {
- mRoundedCornerResDelegate.updateTuningSizeFactor(
- Integer.parseInt(newValue));
- } catch (Exception e) {
- }
+ if (mOverlays == null || !SIZE.equals(key)) {
+ return;
+ }
+ ++mProviderRefreshToken;
+ try {
+ final int sizeFactor = Integer.parseInt(newValue);
+ mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken);
+ } catch (NumberFormatException e) {
+ mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken);
+ }
+ Integer[] filterIds = {
+ R.id.rounded_corner_top_left,
+ R.id.rounded_corner_top_right,
+ R.id.rounded_corner_bottom_left,
+ R.id.rounded_corner_bottom_right
+ };
+ for (final OverlayWindow overlay: mOverlays) {
+ if (overlay == null) {
+ continue;
}
- updateRoundedCornerSize(
- mRoundedCornerResDelegate.getTopRoundedSize(),
- mRoundedCornerResDelegate.getBottomRoundedSize());
+ overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation,
+ mDisplayUniqueId);
}
+ updateHwLayerRoundedCornerSize();
});
}
- private void updateRoundedCornerDrawable() {
- mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
- updateRoundedCornerImageView();
- }
-
- private void updateRoundedCornerImageView() {
- final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable();
- final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable();
-
- if (mScreenDecorHwcLayer != null) {
- mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom);
- return;
- }
-
- if (mOverlays == null) {
- return;
- }
- final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor);
- for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (mOverlays[i] == null) {
- continue;
- }
- final ViewGroup overlayView = mOverlays[i].getRootView();
- ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList);
- ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList);
- ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
- isTopRoundedCorner(i, R.id.left) ? top : bottom);
- ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
- isTopRoundedCorner(i, R.id.right) ? top : bottom);
- }
- }
-
private void updateHwLayerRoundedCornerDrawable() {
if (mScreenDecorHwcLayer == null) {
return;
@@ -1094,25 +994,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable);
}
- @VisibleForTesting
- boolean isTopRoundedCorner(@BoundsPosition int pos, int id) {
- switch (pos) {
- case BOUNDS_POSITION_LEFT:
- case BOUNDS_POSITION_RIGHT:
- if (mRotation == ROTATION_270) {
- return id == R.id.left ? false : true;
- } else {
- return id == R.id.left ? true : false;
- }
- case BOUNDS_POSITION_TOP:
- return true;
- case BOUNDS_POSITION_BOTTOM:
- return false;
- default:
- throw new IllegalArgumentException("Unknown bounds position");
- }
- }
-
private void updateHwLayerRoundedCornerSize() {
if (mScreenDecorHwcLayer == null) {
return;
@@ -1124,28 +1005,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth);
}
- private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) {
-
- if (mScreenDecorHwcLayer != null) {
- mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth());
- return;
- }
-
- if (mOverlays == null) {
- return;
- }
- for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- if (mOverlays[i] == null) {
- continue;
- }
- final ViewGroup overlayView = mOverlays[i].getRootView();
- setSize(overlayView.findViewById(R.id.left),
- isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom);
- setSize(overlayView.findViewById(R.id.right),
- isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom);
- }
- }
-
@VisibleForTesting
protected void setSize(View view, Size pixelSize) {
LayoutParams params = view.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 46a03e809b06..49f758405fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -77,8 +77,8 @@ class ControlsActivity @Inject constructor(
initBroadcastReceiver()
}
- override fun onResume() {
- super.onResume()
+ override fun onStart() {
+ super.onStart()
parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
parent.alpha = 0f
@@ -91,8 +91,8 @@ class ControlsActivity @Inject constructor(
finish()
}
- override fun onPause() {
- super.onPause()
+ override fun onStop() {
+ super.onStop()
uiController.hide()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 166c2654cbaa..59fcf87c5029 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -43,6 +43,7 @@ import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.lowlightclock.LowLightClockController;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarComponent;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
@@ -132,6 +133,7 @@ import dagger.Provides;
},
subcomponents = {
CentralSurfacesComponent.class,
+ NavigationBarComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
index 3543bb4ab9e9..03ee8b11ab41 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.decor
+import android.content.Context
import android.view.DisplayCutout
-import android.view.LayoutInflater
import android.view.Surface
import android.view.View
import android.view.ViewGroup
@@ -38,9 +38,20 @@ abstract class DecorProvider {
/** The aligned bounds for the view which is created through inflateView() */
abstract val alignedBounds: List<Int>
+ /**
+ * Called when res info changed.
+ * Child provider needs to implement it if its view needs to be updated.
+ */
+ abstract fun onReloadResAndMeasure(
+ view: View,
+ reloadToken: Int,
+ @Surface.Rotation rotation: Int,
+ displayUniqueId: String? = null
+ )
+
/** Inflate view into parent as current rotation */
abstract fun inflateView(
- inflater: LayoutInflater,
+ context: Context,
parent: ViewGroup,
@Surface.Rotation rotation: Int
): View
diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
index 9f8679cdea4a..f38ff14726ca 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
@@ -16,31 +16,22 @@
package com.android.systemui.decor
import android.annotation.IdRes
-import android.view.DisplayCutout
-import android.view.LayoutInflater
+import android.content.Context
import android.view.Surface
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.R
-import java.util.HashMap
+import com.android.systemui.RegionInterceptingFrameLayout
-class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) {
+class OverlayWindow(private val context: Context) {
- private val layoutId: Int
- get() {
- return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT ||
- pos == DisplayCutout.BOUNDS_POSITION_TOP) {
- R.layout.rounded_corners_top
- } else {
- R.layout.rounded_corners_bottom
- }
- }
-
- val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup
- private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap()
+ val rootView = RegionInterceptingFrameLayout(context) as ViewGroup
+ private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>()
- fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) {
- val view = decorProvider.inflateView(layoutInflater, rootView, rotation)
+ fun addDecorProvider(
+ decorProvider: DecorProvider,
+ @Surface.Rotation rotation: Int
+ ) {
+ val view = decorProvider.inflateView(context, rootView, rotation)
viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider)
}
@@ -56,4 +47,35 @@ class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos:
viewProviderMap.remove(id)
}
}
+
+ /**
+ * Apply new configuration info into views.
+ * @param filterIds target view ids. Apply to all if null.
+ * @param rotation current or new rotation direction.
+ * @param displayUniqueId new displayUniqueId if any.
+ */
+ fun onReloadResAndMeasure(
+ filterIds: Array<Int>? = null,
+ reloadToken: Int,
+ @Surface.Rotation rotation: Int,
+ displayUniqueId: String? = null
+ ) {
+ filterIds?.forEach { id ->
+ viewProviderMap[id]?.let {
+ it.second.onReloadResAndMeasure(
+ view = it.first,
+ reloadToken = reloadToken,
+ displayUniqueId = displayUniqueId,
+ rotation = rotation)
+ }
+ } ?: run {
+ viewProviderMap.values.forEach {
+ it.second.onReloadResAndMeasure(
+ view = it.first,
+ reloadToken = reloadToken,
+ displayUniqueId = displayUniqueId,
+ rotation = rotation)
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
index 7afd7e0eedc5..136f135af759 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
@@ -16,6 +16,7 @@
package com.android.systemui.decor
+import android.content.Context
import android.content.res.Resources
import android.view.DisplayCutout
import android.view.LayoutInflater
@@ -76,12 +77,21 @@ class PrivacyDotCornerDecorProviderImpl(
private val layoutId: Int
) : CornerDecorProvider() {
+ override fun onReloadResAndMeasure(
+ view: View,
+ reloadToken: Int,
+ rotation: Int,
+ displayUniqueId: String?
+ ) {
+ // Do nothing here because it is handled inside PrivacyDotViewController
+ }
+
override fun inflateView(
- inflater: LayoutInflater,
+ context: Context,
parent: ViewGroup,
@Surface.Rotation rotation: Int
): View {
- inflater.inflate(layoutId, parent, true)
+ LayoutInflater.from(context).inflate(layoutId, parent, true)
return parent.getChildAt(parent.childCount - 1 /* latest new added child */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
new file mode 100644
index 000000000000..4388b8b3b92d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.decor
+
+import android.view.DisplayCutout
+import com.android.systemui.R
+
+class RoundedCornerDecorProviderFactory(
+ private val roundedCornerResDelegate: RoundedCornerResDelegate
+) : DecorProviderFactory() {
+
+ override val hasProviders: Boolean
+ get() = roundedCornerResDelegate.run {
+ // We don't consider isMultipleRadius here because it makes no sense if size is zero.
+ topRoundedSize.width > 0 || bottomRoundedSize.width > 0
+ }
+
+ override val providers: List<DecorProvider>
+ get() {
+ val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0
+ val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0
+ return when {
+ hasTop && hasBottom -> listOf(
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_top_left,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+ roundedCornerResDelegate = roundedCornerResDelegate),
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_top_right,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+ roundedCornerResDelegate = roundedCornerResDelegate),
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_bottom_left,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+ roundedCornerResDelegate = roundedCornerResDelegate),
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_bottom_right,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+ roundedCornerResDelegate = roundedCornerResDelegate)
+ )
+ hasTop -> listOf(
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_top_left,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+ roundedCornerResDelegate = roundedCornerResDelegate),
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_top_right,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+ roundedCornerResDelegate = roundedCornerResDelegate)
+ )
+ hasBottom -> listOf(
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_bottom_left,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+ roundedCornerResDelegate = roundedCornerResDelegate),
+ RoundedCornerDecorProviderImpl(
+ viewId = R.id.rounded_corner_bottom_right,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+ roundedCornerResDelegate = roundedCornerResDelegate)
+ )
+ else -> emptyList()
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
new file mode 100644
index 000000000000..90ff950406b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.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.decor
+
+import android.content.Context
+import android.view.DisplayCutout
+import android.view.Gravity
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import com.android.systemui.R
+
+class RoundedCornerDecorProviderImpl(
+ override val viewId: Int,
+ @DisplayCutout.BoundsPosition override val alignedBound1: Int,
+ @DisplayCutout.BoundsPosition override val alignedBound2: Int,
+ private val roundedCornerResDelegate: RoundedCornerResDelegate
+) : CornerDecorProvider() {
+
+ private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+
+ override fun inflateView(
+ context: Context,
+ parent: ViewGroup,
+ @Surface.Rotation rotation: Int
+ ): View {
+ return ImageView(context).also { view ->
+ // View
+ view.id = viewId
+ initView(view, rotation)
+
+ // LayoutParams
+ val layoutSize = if (isTop) {
+ roundedCornerResDelegate.topRoundedSize
+ } else {
+ roundedCornerResDelegate.bottomRoundedSize
+ }
+ val params = FrameLayout.LayoutParams(
+ layoutSize.width,
+ layoutSize.height,
+ alignedBound1.toLayoutGravity(rotation) or
+ alignedBound2.toLayoutGravity(rotation))
+
+ // AddView
+ parent.addView(view, params)
+ }
+ }
+
+ private fun initView(view: ImageView, @Surface.Rotation rotation: Int) {
+ view.setRoundedCornerImage(roundedCornerResDelegate, isTop)
+ view.adjustRotation(alignedBounds, rotation)
+ view.setColorFilter(IMAGE_TINT_COLOR)
+ }
+
+ override fun onReloadResAndMeasure(
+ view: View,
+ reloadToken: Int,
+ @Surface.Rotation rotation: Int,
+ displayUniqueId: String?
+ ) {
+ roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken)
+
+ initView((view as ImageView), rotation)
+
+ val layoutSize = if (isTop) {
+ roundedCornerResDelegate.topRoundedSize
+ } else {
+ roundedCornerResDelegate.bottomRoundedSize
+ }
+ (view.layoutParams as FrameLayout.LayoutParams).let {
+ it.width = layoutSize.width
+ it.height = layoutSize.height
+ it.gravity = alignedBound1.toLayoutGravity(rotation) or
+ alignedBound2.toLayoutGravity(rotation)
+ view.setLayoutParams(it)
+ }
+ }
+}
+
+private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt()
+
+@DisplayCutout.BoundsPosition
+private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) {
+ Surface.ROTATION_0 -> when (this) {
+ DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT
+ DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP
+ DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT
+ else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM
+ }
+ Surface.ROTATION_90 -> when (this) {
+ DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM
+ DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT
+ DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP
+ else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT
+ }
+ Surface.ROTATION_270 -> when (this) {
+ DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP
+ DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT
+ DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM
+ else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT
+ }
+ else /* Surface.ROTATION_180 */ -> when (this) {
+ DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT
+ DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM
+ DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT
+ else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP
+ }
+}
+
+private fun ImageView.setRoundedCornerImage(
+ resDelegate: RoundedCornerResDelegate,
+ isTop: Boolean
+) {
+ val drawable = if (isTop)
+ resDelegate.topRoundedDrawable
+ else
+ resDelegate.bottomRoundedDrawable
+
+ if (drawable != null) {
+ setImageDrawable(drawable)
+ } else {
+ setImageResource(
+ if (isTop)
+ R.drawable.rounded_corner_top
+ else
+ R.drawable.rounded_corner_bottom
+ )
+ }
+}
+
+/**
+ * Configures the rounded corner drawable's view matrix based on the gravity.
+ *
+ * The gravity describes which corner to configure for, and the drawable we are rotating is assumed
+ * to be oriented for the top-left corner of the device regardless of the target corner.
+ * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
+ * y-axis for the top-right and bottom-left corners.
+ */
+private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) {
+ var newRotation = 0F
+ var newScaleX = 1F
+ var newScaleY = 1F
+
+ val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+ val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)
+ when (rotation) {
+ Surface.ROTATION_0 -> when {
+ isTop && isLeft -> {}
+ isTop && !isLeft -> { newScaleX = -1F }
+ !isTop && isLeft -> { newScaleY = -1F }
+ else /* !isTop && !isLeft */ -> { newRotation = 180F }
+ }
+ Surface.ROTATION_90 -> when {
+ isTop && isLeft -> { newScaleY = -1F }
+ isTop && !isLeft -> {}
+ !isTop && isLeft -> { newRotation = 180F }
+ else /* !isTop && !isLeft */ -> { newScaleX = -1F }
+ }
+ Surface.ROTATION_270 -> when {
+ isTop && isLeft -> { newScaleX = -1F }
+ isTop && !isLeft -> { newRotation = 180F }
+ !isTop && isLeft -> {}
+ else /* !isTop && !isLeft */ -> { newScaleY = -1F }
+ }
+ else /* Surface.ROTATION_180 */ -> when {
+ isTop && isLeft -> { newRotation = 180F }
+ isTop && !isLeft -> { newScaleY = -1F }
+ !isTop && isLeft -> { newScaleX = -1F }
+ else /* !isTop && !isLeft */ -> {}
+ }
+ }
+
+ this.rotation = newRotation
+ this.scaleX = newScaleX
+ this.scaleY = newScaleY
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index c817f89c7a9b..1d38e58dd48c 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -36,6 +36,8 @@ class RoundedCornerResDelegate(
private val density: Float
get() = res.displayMetrics.density
+ private var reloadToken: Int = 0
+
var isMultipleRadius: Boolean = false
private set
@@ -60,12 +62,26 @@ class RoundedCornerResDelegate(
reloadMeasures()
}
- fun reloadAll(newDisplayUniqueId: String?) {
- displayUniqueId = newDisplayUniqueId
+ private fun reloadAll(newReloadToken: Int) {
+ if (reloadToken == newReloadToken) {
+ return
+ }
+ reloadToken = newReloadToken
reloadDrawables()
reloadMeasures()
}
+ fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) {
+ if (displayUniqueId != newDisplayUniqueId) {
+ displayUniqueId = newDisplayUniqueId
+ newReloadToken ?.let { reloadToken = it }
+ reloadDrawables()
+ reloadMeasures()
+ } else {
+ newReloadToken?.let { reloadAll(it) }
+ }
+ }
+
private fun reloadDrawables() {
val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId)
isMultipleRadius = getIsMultipleRadius(configIdx)
@@ -85,34 +101,6 @@ class RoundedCornerResDelegate(
arrayResId = R.array.config_roundedCornerBottomDrawableArray,
backupDrawableId = R.drawable.rounded_corner_bottom
) ?: roundedDrawable
-
- // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
- // (width, height) size of drawable/rounded.xml instead of rounded_corner_radius
- if (isMultipleRadius) {
- roundedSize = Size(
- roundedDrawable?.intrinsicWidth ?: 0,
- roundedDrawable?.intrinsicHeight ?: 0)
- topRoundedDrawable?.let {
- topRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight)
- }
- bottomRoundedDrawable?.let {
- bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight)
- }
- } else {
- val defaultRadius = RoundedCorners.getRoundedCornerRadius(res, displayUniqueId)
- val topRadius = RoundedCorners.getRoundedCornerTopRadius(res, displayUniqueId)
- val bottomRadius = RoundedCorners.getRoundedCornerBottomRadius(res, displayUniqueId)
- roundedSize = Size(defaultRadius, defaultRadius)
- topRoundedSize = Size(topRadius, topRadius)
- bottomRoundedSize = Size(bottomRadius, bottomRadius)
- }
-
- if (topRoundedSize.width == 0) {
- topRoundedSize = roundedSize
- }
- if (bottomRoundedSize.width == 0) {
- bottomRoundedSize = roundedSize
- }
}
private fun reloadMeasures(roundedSizeFactor: Int? = null) {
@@ -137,20 +125,25 @@ class RoundedCornerResDelegate(
bottomRoundedSize = Size(bottomRadius, bottomRadius)
}
- roundedSizeFactor ?.let {
- val length: Int = (it * density).toInt()
- roundedSize = Size(length, length)
- }
-
if (topRoundedSize.width == 0) {
topRoundedSize = roundedSize
}
if (bottomRoundedSize.width == 0) {
bottomRoundedSize = roundedSize
}
+
+ if (roundedSizeFactor != null && roundedSizeFactor > 0) {
+ val length: Int = (roundedSizeFactor * density).toInt()
+ topRoundedSize = Size(length, length)
+ bottomRoundedSize = Size(length, length)
+ }
}
- fun updateTuningSizeFactor(factor: Int) {
+ fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) {
+ if (reloadToken == newReloadToken) {
+ return
+ }
+ reloadToken = newReloadToken
reloadMeasures(factor)
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 0a2e69f943c6..8e1d645d1e97 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -319,9 +319,10 @@ public class DozeLog implements Dumpable {
/**
* Appends the doze state that was suppressed to the doze event log
* @param suppressedState The {@link DozeMachine.State} that was suppressed
+ * @param reason what suppressed always on
*/
- public void traceAlwaysOnSuppressed(DozeMachine.State suppressedState) {
- mLogger.logAlwaysOnSuppressed(suppressedState);
+ public void traceAlwaysOnSuppressed(DozeMachine.State suppressedState, String reason) {
+ mLogger.logAlwaysOnSuppressed(suppressedState, reason);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index f3f6be210fed..4c81563e4f93 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -267,11 +267,12 @@ class DozeLogger @Inject constructor(
})
}
- fun logAlwaysOnSuppressed(state: DozeMachine.State) {
+ fun logAlwaysOnSuppressed(state: DozeMachine.State, reason: String) {
buffer.log(TAG, INFO, {
str1 = state.name
+ str2 = reason
}, {
- "Always-on state suppressed, suppressed state=$str1"
+ "Always-on state suppressed, suppressed state=$str1 reason=$str2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index ae01f0ad84c7..5779bb307790 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -358,8 +358,13 @@ public class DozeMachine {
return State.FINISH;
}
if (mDozeHost.isAlwaysOnSuppressed() && requestedState.isAlwaysOn()) {
- Log.i(TAG, "Doze is suppressed. Suppressing state: " + requestedState);
- mDozeLog.traceAlwaysOnSuppressed(requestedState);
+ Log.i(TAG, "Doze is suppressed by an app. Suppressing state: " + requestedState);
+ mDozeLog.traceAlwaysOnSuppressed(requestedState, "app");
+ return State.DOZE;
+ }
+ if (mDozeHost.isPowerSaveActive() && requestedState.isAlwaysOn()) {
+ Log.i(TAG, "Doze is suppressed by battery saver. Suppressing state: " + requestedState);
+ mDozeLog.traceAlwaysOnSuppressed(requestedState, "batterySaver");
return State.DOZE;
}
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 19b0ea1db04e..b06bf89ffdeb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -136,6 +136,8 @@ public class DozeService extends DreamService
@Override
public void setDozeScreenState(int state) {
super.setDozeScreenState(state);
- mDozeMachine.onScreenState(state);
+ if (mDozeMachine != null) {
+ mDozeMachine.onScreenState(state);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
index 31d43b5475e0..89f50ad9fc21 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
@@ -166,6 +166,8 @@ public class DozeSuppressor implements DozeMachine.Part {
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
public void onPowerSaveChanged(boolean active) {
+ // handles suppression changes, while DozeMachine#transitionPolicy handles gating
+ // transitions to DOZE_AOD
DozeMachine.State nextState = null;
if (mDozeHost.isPowerSaveActive()) {
nextState = DozeMachine.State.DOZE;
@@ -182,6 +184,8 @@ public class DozeSuppressor implements DozeMachine.Part {
@Override
public void onAlwaysOnSuppressedChanged(boolean suppressed) {
+ // handles suppression changes, while DozeMachine#transitionPolicy handles gating
+ // transitions to DOZE_AOD
final DozeMachine.State nextState;
if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
nextState = DozeMachine.State.DOZE_AOD;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index dfbb0c7c1624..db225cf498b0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -26,10 +26,13 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -61,6 +64,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DreamPreviewComplication mPreviewComplication;
+ private final UiEventLogger mUiEventLogger;
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
@@ -97,6 +101,25 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private DreamOverlayStateController mStateController;
+ @VisibleForTesting
+ public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The dream overlay has entered start.")
+ DREAM_OVERLAY_ENTER_START(989),
+ @UiEvent(doc = "The dream overlay has completed start.")
+ DREAM_OVERLAY_COMPLETE_START(990);
+
+ private final int mId;
+
+ DreamOverlayEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
@Inject
public DreamOverlayService(
Context context,
@@ -104,13 +127,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DreamPreviewComplication previewComplication) {
+ DreamPreviewComplication previewComplication,
+ UiEventLogger uiEventLogger) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController;
mPreviewComplication = previewComplication;
+ mUiEventLogger = uiEventLogger;
final DreamOverlayComponent component =
dreamOverlayComponentFactory.create(mViewModelStore, mHost);
@@ -143,6 +168,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
+ mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
setCurrentState(Lifecycle.State.STARTED);
mExecutor.execute(() -> {
if (mDestroyed) {
@@ -159,6 +185,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
mStateController.setOverlayActive(true);
+ mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
index 4e228a14fc88..9b99c522aab9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
@@ -160,9 +160,8 @@ class DreamsSmartspaceController @Inject constructor(
return
}
- // TODO(b/217559844): Replace with "dream" session when available.
val newSession = smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, "lockscreen").build())
+ SmartspaceConfig.Builder(context, "dream").build())
Log.d(TAG, "Starting smartspace session for dream")
newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index e140f6b0faa2..b96cee650663 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -31,8 +31,8 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -89,7 +89,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private VelocityTrackerFactory mVelocityTrackerFactory;
private final GestureDetector.OnGestureListener mOnGestureListener =
- new GestureDetector.SimpleOnGestureListener() {
+ new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
@@ -134,9 +134,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
NotificationShadeWindowController notificationShadeWindowController,
ValueAnimatorCreator valueAnimatorCreator,
VelocityTrackerFactory velocityTrackerFactory,
- @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
- FlingAnimationUtils flingAnimationUtils,
@Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
+ FlingAnimationUtils flingAnimationUtils,
+ @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING)
FlingAnimationUtils flingAnimationUtilsClosing,
@Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
mDisplayMetrics = displayMetrics;
@@ -154,13 +154,16 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
public void getTouchInitiationRegion(Region region) {
if (mCentralSurfaces.isBouncerShowing()) {
region.op(new Rect(0, 0, mDisplayMetrics.widthPixels,
- Math.round(mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
+ Math.round(
+ mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
Region.Op.UNION);
} else {
region.op(new Rect(0,
- Math.round(mDisplayMetrics.heightPixels * (1 - mBouncerZoneScreenPercentage)),
- mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels),
+ Math.round(
+ mDisplayMetrics.heightPixels
+ * (1 - mBouncerZoneScreenPercentage)),
+ mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels),
Region.Op.UNION);
}
}
@@ -191,7 +194,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
final MotionEvent motionEvent = (MotionEvent) event;
- switch(motionEvent.getAction()) {
+ switch (motionEvent.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchSession.pop();
@@ -210,9 +213,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
final float velocityVector =
(float) Math.hypot(horizontalVelocity, verticalVelocity);
-
final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector)
- ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE;
+ ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE;
flingToExpansion(verticalVelocity, expansion);
if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
@@ -236,8 +238,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
}
protected boolean flingRevealsOverlay(float velocity, float velocityVector) {
- // Fully expand if the user has expanded the bouncer less than halfway or final velocity was
- // positive, indicating an downward direction.
+ // Fully expand the space above the bouncer, if the user has expanded the bouncer less
+ // than halfway or final velocity was positive, indicating a downward direction.
if (Math.abs(velocityVector) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
return mCurrentExpansion > FLING_PERCENTAGE_THRESHOLD;
} else {
@@ -246,17 +248,20 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
}
protected void flingToExpansion(float velocity, float expansion) {
+ // The animation utils deal in pixel units, rather than expansion height.
final float viewHeight = mCentralSurfaces.getDisplayHeight();
final float currentHeight = viewHeight * mCurrentExpansion;
final float targetHeight = viewHeight * expansion;
final ValueAnimator animator = createExpansionAnimator(expansion);
if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
- // The animation utils deal in pixel units, rather than expansion height.
- mFlingAnimationUtils.apply(animator, currentHeight, targetHeight, velocity, viewHeight);
+ // Hides the bouncer, i.e., fully expands the space above the bouncer.
+ mFlingAnimationUtilsClosing.apply(animator, currentHeight, targetHeight, velocity,
+ viewHeight);
} else {
- mFlingAnimationUtilsClosing.apply(
- animator, mCurrentExpansion, currentHeight, targetHeight, viewHeight);
+ // Shows the bouncer, i.e., fully collapses the space above the bouncer.
+ mFlingAnimationUtils.apply(
+ animator, currentHeight, targetHeight, velocity, viewHeight);
}
animator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 61cfe925f640..9356b16806f1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -146,7 +146,6 @@ public class Flags {
// 900 - media
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
- public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index af553c744311..acb080a2eaaa 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -1067,13 +1067,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
// Add a little delay before executing, to give the dialog a chance to go away before
// switching user
mHandler.postDelayed(() -> {
- try {
- int currentUserId = getCurrentUser().id;
- mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
- mIActivityManager.stopUser(currentUserId, true /*force*/, null);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't logout user " + re);
- }
+ mDevicePolicyManager.logoutUser();
}, mDialogPressDelay);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index bf464ecdec98..d36bb72e4d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1238,14 +1238,52 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
/**
- * Locks the keyguard if {@link #mPendingLock} is true, unless we're playing the screen off
- * animation.
+ * Locks the keyguard if {@link #mPendingLock} is true, and there are no reasons to further
+ * delay the pending lock.
*
- * If we are, we will lock the keyguard either when the screen off animation ends, or in
- * {@link #onStartedWakingUp} if the animation is cancelled.
+ * If you do delay handling the pending lock, you must ensure that this method is ALWAYS called
+ * again when the condition causing the delay changes. Otherwise, the device may remain unlocked
+ * indefinitely.
*/
public void maybeHandlePendingLock() {
- if (mPendingLock && !mScreenOffAnimationController.isKeyguardShowDelayed()) {
+ if (mPendingLock) {
+
+ // The screen off animation is playing, so if we lock now, the foreground app will
+ // vanish and the keyguard will jump-cut in. Delay it, until either:
+ // - The screen off animation ends. We will call maybeHandlePendingLock from
+ // the end action in UnlockedScreenOffAnimationController#animateInKeyguard.
+ // - The screen off animation is cancelled by the device waking back up. We will call
+ // maybeHandlePendingLock from KeyguardViewMediator#onStartedWakingUp.
+ if (mScreenOffAnimationController.isKeyguardShowDelayed()) {
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: not handling because the screen off "
+ + "animation's isKeyguardShowDelayed() returned true. This should be "
+ + "handled soon by #onStartedWakingUp, or by the end actions of the "
+ + "screen off animation.");
+ }
+
+ return;
+ }
+
+ // The device was re-locked while in the process of unlocking. If we lock now, callbacks
+ // in the unlock sequence might end up re-unlocking the device. Delay the lock until the
+ // keyguard is done going away. We'll call maybeHandlePendingLock again in
+ // StatusBar#finishKeyguardFadingAway, which is always responsible for setting
+ // isKeyguardGoingAway to false.
+ if (mKeyguardStateController.isKeyguardGoingAway()) {
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: not handling because the keyguard is "
+ + "going away. This should be handled shortly by "
+ + "StatusBar#finishKeyguardFadingAway.");
+ }
+
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "#maybeHandlePendingLock: handling pending lock; locking keyguard.");
+ }
+
doKeyguardLocked(null);
mPendingLock = false;
}
@@ -1669,16 +1707,11 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
return;
}
- // If the keyguard is already showing, don't bother unless it was in the process of going
- // away. If it was going away, keyguard state may be out of sync and we should make sure to
- // re-show it explicitly. Check flags in both files to account for the hiding animation
- // which results in a delay and discrepancy between flags.
- if ((mShowing && mKeyguardViewControllerLazy.get().isShowing())
- && !mKeyguardStateController.isKeyguardGoingAway()) {
- if (DEBUG) {
- Log.d(TAG, "doKeyguard: not showing "
- + "because it is already showing and not going away");
- }
+ // if the keyguard is already showing, don't bother. check flags in both files
+ // to account for the hiding animation which results in a delay and discrepancy
+ // between flags
+ if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
}
@@ -2185,14 +2218,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mKeyguardExitAnimationRunner = null;
mScreenOnCoordinator.setWakeAndUnlocking(false);
mPendingLock = false;
-
- // If we're asked to re-show while the keyguard is going away, force callbacks to ensure
- // that state is re-set correctly. Otherwise, we might short circuit since mShowing is
- // true during the keyguard going away process, despite having partially set some state
- // to unlocked.
- setShowingLocked(
- true, mKeyguardStateController.isKeyguardGoingAway() /* forceCallbacks */);
-
+ setShowingLocked(true);
mKeyguardViewControllerLazy.get().show(options);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2347,8 +2373,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here, so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
- mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw(
- false /* syncBuffer */);
+ mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
mScreenOnCoordinator.setWakeAndUnlocking(false);
}
@@ -2363,28 +2388,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
@Override
public void onAnimationFinished() throws RemoteException {
try {
- // WindowManager always needs to know that this animation
- // finished so it does not wait the 10s until timeout.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
-
- // If we're not interactive, it means the device is going back to
- // sleep. This happens if the power button is pressed during the
- // activity launch. If we're going back to sleep, we should *not*
- // run keyguard exit finished callbacks and hide the keyguard, since
- // we are in the process of locking again and this might result in
- // the device staying unlocked when it shouldn't.
- // We need to directly query isInteractive rather than mGoingToSleep
- // because mGoingToSleep is set in onStartedGoingToSleep, which is
- // dispatched asynchronously.
- if (mPM.isInteractive()) {
- onKeyguardExitFinished();
- mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
- 0 /* fadeoutDuration */);
- mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
- }
+ onKeyguardExitFinished();
+ mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
+ 0 /* fadeoutDuration */);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index c3f4ce986596..5a8b7e3d3d64 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -45,8 +45,7 @@ class KeyguardMediaController @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
private val context: Context,
- configurationController: ConfigurationController,
- private val mediaFlags: MediaFlags
+ configurationController: ConfigurationController
) {
init {
@@ -62,11 +61,7 @@ class KeyguardMediaController @Inject constructor(
})
// First let's set the desired state that we want for this host
- mediaHost.expansion = if (mediaFlags.useMediaSessionLayout()) {
- MediaHostState.EXPANDED
- } else {
- MediaHostState.COLLAPSED
- }
+ mediaHost.expansion = MediaHostState.EXPANDED
mediaHost.showsOnlyActiveMedia = true
mediaHost.falsingProtectionNeeded = true
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 83ad027baf28..20029fec4dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -419,15 +419,8 @@ class MediaCarouselController @Inject constructor(
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
if (existingPlayer == null) {
var newPlayer = mediaControlPanelFactory.get()
- if (mediaFlags.useMediaSessionLayout()) {
- newPlayer.attachPlayer(
- PlayerSessionViewHolder.create(LayoutInflater.from(context), mediaContent),
- MediaViewController.TYPE.PLAYER_SESSION)
- } else {
- newPlayer.attachPlayer(
- PlayerViewHolder.create(LayoutInflater.from(context), mediaContent),
- MediaViewController.TYPE.PLAYER)
- }
+ newPlayer.attachPlayer(MediaViewHolder.create(
+ LayoutInflater.from(context), mediaContent))
newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
@@ -522,7 +515,7 @@ class MediaCarouselController @Inject constructor(
private fun recreatePlayers() {
bgColor = getBackgroundColor()
- pageIndicator.tintList = ColorStateList.valueOf(getForegroundColor())
+ pageIndicator.tintList = ColorStateList.valueOf(R.color.material_dynamic_neutral_variant80)
MediaPlayerData.mediaData().forEach { (key, data, isSsMediaRec) ->
if (isSsMediaRec) {
@@ -543,14 +536,6 @@ class MediaCarouselController @Inject constructor(
return context.getColor(R.color.material_dynamic_secondary95)
}
- private fun getForegroundColor(): Int {
- return if (mediaFlags.useMediaSessionLayout()) {
- context.getColor(R.color.material_dynamic_neutral_variant80)
- } else {
- context.getColor(R.color.material_dynamic_secondary10)
- }
- }
-
private fun updatePageIndicator() {
val numPages = mediaContent.getChildCount()
pageIndicator.setNumPages(numPages)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index ffdd5376b12e..958c2441942c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -35,7 +35,6 @@ import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Process;
-import android.text.Layout;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -65,7 +64,6 @@ import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -96,22 +94,22 @@ public class MediaControlPanel {
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
- // Button IDs for QS controls
- static final int[] ACTION_IDS = {
- R.id.action0,
- R.id.action1,
- R.id.action2,
- R.id.action3,
- R.id.action4
- };
-
// Buttons to show in small player when using semantic actions
- private static final List<Integer> SEMANTIC_ACTION_IDS = List.of(
+ private static final List<Integer> SEMANTIC_ACTIONS_COMPACT = List.of(
R.id.actionPlayPause,
R.id.actionPrev,
R.id.actionNext
);
+ // Buttons to show in small player when using semantic actions
+ private static final List<Integer> SEMANTIC_ACTIONS_ALL = List.of(
+ R.id.actionPlayPause,
+ R.id.actionPrev,
+ R.id.actionNext,
+ R.id.action0,
+ R.id.action1
+ );
+
private final SeekBarViewModel mSeekBarViewModel;
private SeekBarObserver mSeekBarObserver;
protected final Executor mBackgroundExecutor;
@@ -127,8 +125,6 @@ public class MediaControlPanel {
private MediaController mController;
private Lazy<MediaDataManager> mMediaDataManagerLazy;
private int mBackgroundColor;
- private int mDevicePadding;
- private int mAlbumArtSize;
// Instance id for logging purpose.
protected int mInstanceId = -1;
// Uid for the media app.
@@ -167,7 +163,6 @@ public class MediaControlPanel {
mMediaCarouselController = mediaCarouselController;
mFalsingManager = falsingManager;
mSystemClock = systemClock;
- loadDimens();
mSeekBarViewModel.setLogSmartspaceClick(() -> {
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
@@ -184,12 +179,6 @@ public class MediaControlPanel {
mMediaViewController.onDestroy();
}
- private void loadDimens() {
- mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size);
- mDevicePadding = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_media_album_device_padding);
- }
-
/**
* Get the view holder used to display media controls.
*
@@ -241,15 +230,14 @@ public class MediaControlPanel {
}
/** Attaches the player to the player view holder. */
- public void attachPlayer(MediaViewHolder vh, MediaViewController.TYPE playerType) {
+ public void attachPlayer(MediaViewHolder vh) {
mMediaViewHolder = vh;
TransitionLayout player = vh.getPlayer();
- boolean useSessionLayout = playerType == MediaViewController.TYPE.PLAYER_SESSION;
- mSeekBarObserver = new SeekBarObserver(vh, useSessionLayout);
+ mSeekBarObserver = new SeekBarObserver(vh);
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
- mMediaViewController.attach(player, playerType);
+ mMediaViewController.attach(player, MediaViewController.TYPE.PLAYER);
vh.getPlayer().setOnLongClickListener(v -> {
if (!mMediaViewController.isGutsVisible()) {
@@ -305,16 +293,6 @@ public class MediaControlPanel {
if (mMediaViewHolder == null) {
return;
}
- bindPlayerCommon(data, key);
- if (mMediaViewHolder instanceof PlayerViewHolder) {
- bindNotificationPlayer(data, key);
- } else if (mMediaViewHolder instanceof PlayerSessionViewHolder) {
- bindSessionPlayer(data, key);
- }
- }
-
- /** Bind elements common to both layouts */
- private void bindPlayerCommon(@NonNull MediaData data, String key) {
mKey = key;
MediaSession.Token token = data.getToken();
PackageManager packageManager = mContext.getPackageManager();
@@ -371,18 +349,24 @@ public class MediaControlPanel {
final MediaController controller = getController();
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
- // Guts label
- boolean isDismissible = data.isClearable();
- mMediaViewHolder.getLongPressText().setText(isDismissible
- ? R.string.controls_media_close_session
- : R.string.controls_media_active_session);
+ bindOutputSwitcherChip(data);
+ bindLongPressMenu(data);
+ bindActionButtons(data);
+ bindArtworkAndColors(data);
+
+ // TODO: We don't need to refresh this state constantly, only if the state actually changed
+ // to something which might impact the measurement
+ mMediaViewController.refreshState();
+ }
+ private void bindOutputSwitcherChip(MediaData data) {
// Output switcher chip
ViewGroup seamlessView = mMediaViewHolder.getSeamless();
seamlessView.setVisibility(View.VISIBLE);
ImageView iconView = mMediaViewHolder.getSeamlessIcon();
TextView deviceName = mMediaViewHolder.getSeamlessText();
final MediaDeviceData device = data.getDevice();
+
// Disable clicking on output switcher for invalid devices and resumption controls
final boolean seamlessDisabled = (device != null && !device.getEnabled())
|| data.getResumption();
@@ -426,9 +410,20 @@ public class MediaControlPanel {
mMediaOutputDialogFactory.create(data.getPackageName(), true,
mMediaViewHolder.getSeamlessButton());
}
- });
+ });
+ }
- // Dismiss
+ private void bindLongPressMenu(MediaData data) {
+ boolean isDismissible = data.isClearable();
+ String dismissText;
+ if (isDismissible) {
+ dismissText = mContext.getString(R.string.controls_media_close_session, data.getApp());
+ } else {
+ dismissText = mContext.getString(R.string.controls_media_active_session);
+ }
+ mMediaViewHolder.getLongPressText().setText(dismissText);
+
+ // Dismiss button
mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
mMediaViewHolder.getDismiss().setEnabled(isDismissible);
mMediaViewHolder.getDismiss().setOnClickListener(v -> {
@@ -443,136 +438,16 @@ public class MediaControlPanel {
MediaViewController.GUTS_ANIMATION_DURATION + 100)) {
Log.w(TAG, "Manager failed to dismiss media " + mKey);
// Remove directly from carousel so user isn't stuck with defunct controls
- mMediaCarouselController.removePlayer(key, false, false);
+ mMediaCarouselController.removePlayer(mKey, false, false);
}
} else {
Log.w(TAG, "Dismiss media with null notification. Token uid="
+ data.getToken().getUid());
}
});
-
- // TODO: We don't need to refresh this state constantly, only if the state actually changed
- // to something which might impact the measurement
- mMediaViewController.refreshState();
}
- /** Bind elements specific to PlayerViewHolder */
- private void bindNotificationPlayer(@NonNull MediaData data, String key) {
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
-
- // Album art
- ImageView albumView = mMediaViewHolder.getAlbumView();
- boolean hasArtwork = data.getArtwork() != null;
- if (hasArtwork) {
- Drawable artwork = getScaledThumbnail(data.getArtwork());
- albumView.setPadding(0, 0, 0, 0);
- albumView.setImageDrawable(artwork);
- } else {
- Drawable deviceIcon;
- if (data.getDevice() != null && data.getDevice().getIcon() != null) {
- deviceIcon = data.getDevice().getIcon().getConstantState().newDrawable().mutate();
- } else {
- deviceIcon = getContext().getDrawable(R.drawable.ic_headphone);
- }
- deviceIcon.setTintList(ColorStateList.valueOf(mBackgroundColor));
- albumView.setPadding(mDevicePadding, mDevicePadding, mDevicePadding, mDevicePadding);
- albumView.setImageDrawable(deviceIcon);
- }
-
- // App icon - use notification icon
- ImageView appIconView = mMediaViewHolder.getAppIcon();
- appIconView.clearColorFilter();
- if (data.getAppIcon() != null && !data.getResumption()) {
- appIconView.setImageIcon(data.getAppIcon());
- int color = mContext.getColor(R.color.material_dynamic_secondary10);
- appIconView.setColorFilter(color);
- } else {
- // Resume players use launcher icon
- appIconView.setColorFilter(getGrayscaleFilter());
- try {
- Drawable icon = mContext.getPackageManager().getApplicationIcon(
- data.getPackageName());
- appIconView.setImageDrawable(icon);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
- appIconView.setImageResource(R.drawable.ic_music_note);
- }
- }
-
- // Media action buttons
- List<MediaAction> actionIcons = data.getActions();
- List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
-
- // If we got session actions, use those instead
- if (data.getSemanticActions() != null) {
- MediaButton semanticActions = data.getSemanticActions();
-
- actionIcons = new ArrayList<MediaAction>();
- actionIcons.add(semanticActions.getCustom0());
- actionIcons.add(semanticActions.getPrevOrCustom());
- actionIcons.add(semanticActions.getPlayOrPause());
- actionIcons.add(semanticActions.getNextOrCustom());
- actionIcons.add(semanticActions.getCustom1());
-
- actionsWhenCollapsed = new ArrayList<Integer>();
- actionsWhenCollapsed.add(1);
- actionsWhenCollapsed.add(2);
- actionsWhenCollapsed.add(3);
- }
-
- int i = 0;
- for (; i < actionIcons.size() && i < ACTION_IDS.length; i++) {
- int actionId = ACTION_IDS[i];
- boolean visibleInCompat = actionsWhenCollapsed.contains(i);
- final ImageButton button = mMediaViewHolder.getAction(actionId);
- MediaAction mediaAction = actionIcons.get(i);
- if (mediaAction != null) {
- button.setImageIcon(mediaAction.getIcon());
- button.setContentDescription(mediaAction.getContentDescription());
- Runnable action = mediaAction.getAction();
-
- if (action == null) {
- button.setEnabled(false);
- } else {
- button.setEnabled(true);
- button.setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
- /* isRecommendationCard */ false);
- action.run();
- }
- });
- }
- setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
- setVisibleAndAlpha(expandedSet, actionId, true /*visible */);
- } else {
- button.setImageIcon(null);
- button.setContentDescription(null);
- button.setEnabled(false);
- setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
- // for expanded layout, set as INVISIBLE so that we still reserve space in the UI
- expandedSet.setVisibility(actionId, ConstraintSet.INVISIBLE);
- expandedSet.setAlpha(actionId, 0.0f);
- }
- }
-
- // Hide any unused buttons
- for (; i < ACTION_IDS.length; i++) {
- setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
- setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /* visible */);
- }
- // If no actions, set the first view as INVISIBLE so expanded height remains constant
- if (actionIcons.size() == 0) {
- expandedSet.setVisibility(ACTION_IDS[0], ConstraintSet.INVISIBLE);
- }
- }
-
- /** Bind elements specific to PlayerSessionViewHolder */
- private void bindSessionPlayer(@NonNull MediaData data, String key) {
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
-
+ private void bindArtworkAndColors(MediaData data) {
// Default colors
int surfaceColor = mBackgroundColor;
int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
@@ -592,7 +467,7 @@ public class MediaControlPanel {
boolean hasArtwork = data.getArtwork() != null;
if (hasArtwork) {
colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()),
- true);
+ true);
// Scale artwork to fit background
int width = mMediaViewHolder.getPlayer().getWidth();
@@ -661,6 +536,14 @@ public class MediaControlPanel {
seekbar.setProgressTintList(textColorList);
seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary));
+ // Action buttons
+ mMediaViewHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
+ mMediaViewHolder.getActionPlayPause().setImageTintList(
+ ColorStateList.valueOf(textPrimaryInverse));
+ for (ImageButton button : mMediaViewHolder.getTransparentActionButtons()) {
+ button.setImageTintList(textColorList);
+ }
+
// Output switcher
View seamlessView = mMediaViewHolder.getSeamlessButton();
seamlessView.setBackgroundTintList(accentColorList);
@@ -669,21 +552,26 @@ public class MediaControlPanel {
TextView seamlessText = mMediaViewHolder.getSeamlessText();
seamlessText.setTextColor(surfaceColor);
- // Media action buttons
+ // Long press buttons
+ mMediaViewHolder.getLongPressText().setTextColor(textColorList);
+ mMediaViewHolder.getSettings().setImageTintList(accentColorList);
+ mMediaViewHolder.getCancelText().setTextColor(textColorList);
+ mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList);
+ mMediaViewHolder.getDismissText().setTextColor(surfaceColor);
+ mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
+ }
+
+ private void bindActionButtons(MediaData data) {
MediaButton semanticActions = data.getSemanticActions();
- PlayerSessionViewHolder sessionHolder = (PlayerSessionViewHolder) mMediaViewHolder;
ImageButton[] genericButtons = new ImageButton[]{
- sessionHolder.getAction0(),
- sessionHolder.getAction1(),
- sessionHolder.getAction2(),
- sessionHolder.getAction3(),
- sessionHolder.getAction4()};
-
- ImageButton[] semanticButtons = new ImageButton[]{
- sessionHolder.getActionPlayPause(),
- sessionHolder.getActionNext(),
- sessionHolder.getActionPrev()};
+ mMediaViewHolder.getAction0(),
+ mMediaViewHolder.getAction1(),
+ mMediaViewHolder.getAction2(),
+ mMediaViewHolder.getAction3(),
+ mMediaViewHolder.getAction4()};
+ ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
+ ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
if (semanticActions != null) {
// Hide all the generic buttons
for (ImageButton b: genericButtons) {
@@ -691,22 +579,15 @@ public class MediaControlPanel {
setVisibleAndAlpha(expandedSet, b.getId(), false);
}
- // Play/pause button has a background
- sessionHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
- setSemanticButton(sessionHolder.getActionPlayPause(), semanticActions.getPlayOrPause(),
- ColorStateList.valueOf(textPrimaryInverse), collapsedSet, expandedSet, true);
-
- setSemanticButton(sessionHolder.getActionNext(), semanticActions.getNextOrCustom(),
- textColorList, collapsedSet, expandedSet, true);
- setSemanticButton(sessionHolder.getActionPrev(), semanticActions.getPrevOrCustom(),
- textColorList, collapsedSet, expandedSet, true);
- setSemanticButton(sessionHolder.getAction0(), semanticActions.getCustom0(),
- textColorList, collapsedSet, expandedSet, false);
- setSemanticButton(sessionHolder.getAction1(), semanticActions.getCustom1(),
- textColorList, collapsedSet, expandedSet, false);
+ for (int id : SEMANTIC_ACTIONS_ALL) {
+ boolean showInCompact = SEMANTIC_ACTIONS_COMPACT.contains(id);
+ ImageButton button = mMediaViewHolder.getAction(id);
+ MediaAction action = semanticActions.getActionById(id);
+ setSemanticButton(button, action, collapsedSet, expandedSet, showInCompact);
+ }
} else {
- // Hide all the semantic buttons
- for (int id : SEMANTIC_ACTION_IDS) {
+ // Hide buttons that only appear for semantic actions
+ for (int id : SEMANTIC_ACTIONS_COMPACT) {
setVisibleAndAlpha(collapsedSet, id, false);
setVisibleAndAlpha(expandedSet, id, false);
}
@@ -717,13 +598,12 @@ public class MediaControlPanel {
int i = 0;
for (; i < actions.size(); i++) {
boolean showInCompact = actionsWhenCollapsed.contains(i);
- setSemanticButton(genericButtons[i], actions.get(i), textColorList, collapsedSet,
+ setSemanticButton(genericButtons[i], actions.get(i), collapsedSet,
expandedSet, showInCompact);
}
for (; i < 5; i++) {
// Hide any unused buttons
- setSemanticButton(genericButtons[i], null, textColorList, collapsedSet,
- expandedSet, false);
+ setSemanticButton(genericButtons[i], null, collapsedSet, expandedSet, false);
}
}
@@ -732,21 +612,10 @@ public class MediaControlPanel {
expandedSet.setVisibility(R.id.media_progress_bar,
seekbarEnabled ? ConstraintSet.VISIBLE : ConstraintSet.INVISIBLE);
expandedSet.setAlpha(R.id.media_progress_bar, seekbarEnabled ? 1.0f : 0.0f);
-
- // Long press buttons
- mMediaViewHolder.getLongPressText().setTextColor(textColorList);
- mMediaViewHolder.getSettingsText().setTextColor(textColorList);
- mMediaViewHolder.getSettingsText().setBackgroundTintList(accentColorList);
- mMediaViewHolder.getCancelText().setTextColor(textColorList);
- mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList);
- mMediaViewHolder.getDismissText().setTextColor(textColorList);
- mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
}
private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
- ColorStateList fgColor, ConstraintSet collapsedSet, ConstraintSet expandedSet,
- boolean showInCompact) {
- button.setImageTintList(fgColor);
+ ConstraintSet collapsedSet, ConstraintSet expandedSet, boolean showInCompact) {
if (mediaAction != null) {
button.setImageIcon(mediaAction.getIcon());
button.setContentDescription(mediaAction.getContentDescription());
@@ -982,60 +851,15 @@ public class MediaControlPanel {
}
private void openGuts() {
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
-
- boolean wasTruncated = false;
- Layout l = null;
if (mMediaViewHolder != null) {
mMediaViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
- l = mMediaViewHolder.getSettingsText().getLayout();
} else if (mRecommendationViewHolder != null) {
mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
- l = mRecommendationViewHolder.getSettingsText().getLayout();
- }
- if (l != null) {
- wasTruncated = l.getEllipsisCount(0) > 0;
}
- mMediaViewController.setShouldHideGutsSettings(wasTruncated);
- if (wasTruncated) {
- // not enough room for the settings button to show fully, let's hide it
- expandedSet.constrainMaxWidth(R.id.settings, 0);
- collapsedSet.constrainMaxWidth(R.id.settings, 0);
- }
-
mMediaViewController.openGuts();
}
/**
- * Scale drawable to fit into the square album art thumbnail
- */
- @UiThread
- private Drawable getScaledThumbnail(Icon icon) {
- if (icon == null) {
- return null;
- }
- // Let's scale down the View, such that the content always nicely fills the view.
- // ThumbnailUtils actually scales it down such that it may not be filled for odd aspect
- // ratios
- Drawable drawable = icon.loadDrawable(mContext);
- float aspectRatio = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth();
- Rect bounds;
- if (aspectRatio > 1.0f) {
- bounds = new Rect(0, 0, mAlbumArtSize, (int) (mAlbumArtSize * aspectRatio));
- } else {
- bounds = new Rect(0, 0, (int) (mAlbumArtSize / aspectRatio), mAlbumArtSize);
- }
- if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
- float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
- float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
- bounds.offset((int) -offsetX, (int) -offsetY);
- }
- drawable.setBounds(bounds);
- return drawable;
- }
-
- /**
* Scale artwork to fill the background of the panel
*/
@UiThread
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 4cf6291fe35b..f1712dbc3f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -20,6 +20,7 @@ import android.app.PendingIntent
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.session.MediaSession
+import com.android.systemui.R
/** State of a media view. */
data class MediaData(
@@ -154,7 +155,18 @@ data class MediaButton(
* Second custom action space
*/
var custom1: MediaAction? = null
-)
+) {
+ fun getActionById(id: Int): MediaAction? {
+ return when (id) {
+ R.id.actionPlayPause -> playOrPause
+ R.id.actionNext -> nextOrCustom
+ R.id.actionPrev -> prevOrCustom
+ R.id.action0 -> custom0
+ R.id.action1 -> custom1
+ else -> null
+ }
+ }
+}
/** State of a media action. */
data class MediaAction(
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
index 59237d936d72..b85ae4820d49 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
@@ -35,13 +35,6 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
}
/**
- * Check whether media controls should use the new session-based layout
- */
- fun useMediaSessionLayout(): Boolean {
- return featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT)
- }
-
- /**
* Check whether we support displaying information about mute await connections.
*/
fun areMuteAwaitConnectionsEnabled() = featureFlags.isEnabled(Flags.MEDIA_MUTE_AWAIT)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index d472aeee1073..d978d02d63f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -98,6 +98,10 @@ class MediaHierarchyManager @Inject constructor(
private var currentBounds = Rect()
private var animationStartBounds: Rect = Rect()
+ private var animationStartClipping = Rect()
+ private var currentClipping = Rect()
+ private var targetClipping = Rect()
+
/**
* The cross fade progress at the start of the animation. 0.5f means it's just switching between
* the start and the end location and the content is fully faded, while 0.75f means that we're
@@ -144,7 +148,8 @@ class MediaHierarchyManager @Inject constructor(
}
interpolateBounds(animationStartBounds, targetBounds, boundsProgress,
result = currentBounds)
- applyState(currentBounds, currentAlpha)
+ resolveClipping(currentClipping)
+ applyState(currentBounds, currentAlpha, clipBounds = currentClipping)
}
addListener(object : AnimatorListenerAdapter() {
private var cancelled: Boolean = false
@@ -169,6 +174,12 @@ class MediaHierarchyManager @Inject constructor(
})
}
+ private fun resolveClipping(result: Rect) {
+ if (animationStartClipping.isEmpty) result.set(targetClipping)
+ else if (targetClipping.isEmpty) result.set(animationStartClipping)
+ else result.setIntersect(animationStartClipping, targetClipping)
+ }
+
private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_DREAM_OVERLAY + 1)
/**
* The last location where this view was at before going to the desired location. This is
@@ -629,10 +640,12 @@ class MediaHierarchyManager @Inject constructor(
// We also go in here in case the view was detached, since the bounds wouldn't
// be correct anymore
animationStartBounds.set(currentBounds)
+ animationStartClipping.set(currentClipping)
} else {
// otherwise, let's take the freshest state, since the current one could
// be outdated
animationStartBounds.set(previousHost.currentBounds)
+ animationStartClipping.set(previousHost.currentClipping)
}
val transformationType = calculateTransformationType()
var needsCrossFade = transformationType == TRANSFORMATION_TYPE_FADE
@@ -745,7 +758,7 @@ class MediaHierarchyManager @Inject constructor(
// Let's immediately apply the target state (which is interpolated) if there is
// no animation running. Otherwise the animation update will already update
// the location
- applyState(targetBounds, carouselAlpha)
+ applyState(targetBounds, carouselAlpha, clipBounds = targetClipping)
}
}
@@ -769,9 +782,11 @@ class MediaHierarchyManager @Inject constructor(
val newBounds = endHost.currentBounds
val previousBounds = starthost.currentBounds
targetBounds = interpolateBounds(previousBounds, newBounds, progress)
+ targetClipping = endHost.currentClipping
} else if (endHost != null) {
val bounds = endHost.currentBounds
targetBounds.set(bounds)
+ targetClipping = endHost.currentClipping
}
}
@@ -879,8 +894,14 @@ class MediaHierarchyManager @Inject constructor(
/**
* Apply the current state to the view, updating it's bounds and desired state
*/
- private fun applyState(bounds: Rect, alpha: Float, immediately: Boolean = false) {
+ private fun applyState(
+ bounds: Rect,
+ alpha: Float,
+ immediately: Boolean = false,
+ clipBounds: Rect = EMPTY_RECT
+ ) {
currentBounds.set(bounds)
+ currentClipping = clipBounds
carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
val startLocation = if (onlyUseEndState) -1 else previousLocation
@@ -889,6 +910,10 @@ class MediaHierarchyManager @Inject constructor(
mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
updateHostAttachment()
if (currentAttachmentLocation == IN_OVERLAY) {
+ // Setting the clipping on the hierarchy of `mediaFrame` does not work
+ if (!currentClipping.isEmpty) {
+ currentBounds.intersect(currentClipping)
+ }
mediaFrame.setLeftTopRightBottom(
currentBounds.left,
currentBounds.top,
@@ -1113,6 +1138,7 @@ class MediaHierarchyManager @Inject constructor(
const val TRANSFORMATION_TYPE_FADE = 1
}
}
+private val EMPTY_RECT = Rect()
@IntDef(prefix = ["TRANSFORMATION_TYPE_"], value = [
MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index eb209f723cb5..d08b6f825f41 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -55,6 +55,13 @@ class MediaHost constructor(
return field
}
+ /**
+ * Set the clipping that this host should use, based on its parent's bounds.
+ *
+ * Use [Rect.set].
+ */
+ val currentClipping = Rect()
+
private val listener = object : MediaDataManager.Listener {
override fun onMediaDataLoaded(
key: String,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index a60016b23a7c..5210499ca311 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -22,7 +22,10 @@ import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.animation.*
+import com.android.systemui.util.animation.MeasurementOutput
+import com.android.systemui.util.animation.TransitionLayout
+import com.android.systemui.util.animation.TransitionLayoutController
+import com.android.systemui.util.animation.TransitionViewState
import javax.inject.Inject
/**
@@ -40,7 +43,7 @@ class MediaViewController @Inject constructor(
* session-based player, or recommendation
*/
enum class TYPE {
- PLAYER, PLAYER_SESSION, RECOMMENDATION
+ PLAYER, RECOMMENDATION
}
companion object {
@@ -186,11 +189,6 @@ class MediaViewController @Inject constructor(
var isGutsVisible = false
private set
- /**
- * Whether the settings button in the guts should be visible
- */
- var shouldHideGutsSettings = false
-
init {
mediaHostStatesManager.addController(this)
layoutController.sizeChangedListener = { width: Int, height: Int ->
@@ -259,13 +257,11 @@ class MediaViewController @Inject constructor(
*/
private fun setGutsViewState(viewState: TransitionViewState) {
val controlsIds = when (type) {
- TYPE.PLAYER -> PlayerViewHolder.controlsIds
- TYPE.PLAYER_SESSION -> PlayerSessionViewHolder.controlsIds
+ TYPE.PLAYER -> MediaViewHolder.controlsIds
TYPE.RECOMMENDATION -> RecommendationViewHolder.controlsIds
}
val gutsIds = when (type) {
- TYPE.PLAYER -> PlayerViewHolder.gutsIds
- TYPE.PLAYER_SESSION -> PlayerSessionViewHolder.gutsIds
+ TYPE.PLAYER -> MediaViewHolder.gutsIds
TYPE.RECOMMENDATION -> RecommendationViewHolder.gutsIds
}
controlsIds.forEach { id ->
@@ -279,23 +275,22 @@ class MediaViewController @Inject constructor(
viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f
viewState.widgetStates.get(id)?.gone = !isGutsVisible
}
- if (shouldHideGutsSettings) {
- viewState.widgetStates.get(R.id.settings)?.gone = true
- }
}
/**
* Apply squishFraction to a copy of viewState such that the cached version is untouched.
*/
- private fun squishViewState(viewState: TransitionViewState,
- squishFraction: Float): TransitionViewState {
+ private fun squishViewState(
+ viewState: TransitionViewState,
+ squishFraction: Float
+ ): TransitionViewState {
val squishedViewState = viewState.copy()
squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
val albumArtViewState = viewState.widgetStates.get(R.id.album_art)
if (albumArtViewState != null) {
albumArtViewState.height = squishedViewState.height
}
- return squishedViewState;
+ return squishedViewState
}
/**
@@ -314,7 +309,7 @@ class MediaViewController @Inject constructor(
if (viewState != null) {
// we already have cached this measurement, let's continue
if (state.squishFraction < 1f) {
- return squishViewState(viewState, state.squishFraction);
+ return squishViewState(viewState, state.squishFraction)
}
return viewState
}
@@ -351,7 +346,7 @@ class MediaViewController @Inject constructor(
state.expansion)
}
if (state.squishFraction < 1f) {
- return squishViewState(result, state.squishFraction);
+ return squishViewState(result, state.squishFraction)
}
return result
}
@@ -492,10 +487,6 @@ class MediaViewController @Inject constructor(
// These XML resources contain ConstraintSets that will apply to this player type's layout
when (type) {
TYPE.PLAYER -> {
- collapsedLayout.load(context, R.xml.media_collapsed)
- expandedLayout.load(context, R.xml.media_expanded)
- }
- TYPE.PLAYER_SESSION -> {
collapsedLayout.load(context, R.xml.media_session_collapsed)
expandedLayout.load(context, R.xml.media_session_expanded)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index 5f606969153c..836b5cb3e2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media
import android.util.Log
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
@@ -29,9 +30,9 @@ import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "MediaViewHolder"
/**
- * Parent class for different media player views
+ * Holder class for media player view
*/
-abstract class MediaViewHolder constructor(itemView: View) {
+class MediaViewHolder constructor(itemView: View) {
val player = itemView as TransitionLayout
// Player information
@@ -57,10 +58,12 @@ abstract class MediaViewHolder constructor(itemView: View) {
val cancelText = itemView.requireViewById<TextView>(R.id.cancel_text)
val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
val dismissText = itemView.requireViewById<TextView>(R.id.dismiss_text)
- val settings = itemView.requireViewById<View>(R.id.settings)
- val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
+ val settings = itemView.requireViewById<ImageButton>(R.id.settings)
// Action Buttons
+ val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
+ val actionNext = itemView.requireViewById<ImageButton>(R.id.actionNext)
+ val actionPrev = itemView.requireViewById<ImageButton>(R.id.actionPrev)
val action0 = itemView.requireViewById<ImageButton>(R.id.action0)
val action1 = itemView.requireViewById<ImageButton>(R.id.action1)
val action2 = itemView.requireViewById<ImageButton>(R.id.action2)
@@ -73,6 +76,9 @@ abstract class MediaViewHolder constructor(itemView: View) {
it.registerLightSource(cancel)
it.registerLightSource(dismiss)
it.registerLightSource(settings)
+ it.registerLightSource(actionPlayPause)
+ it.registerLightSource(actionNext)
+ it.registerLightSource(actionPrev)
it.registerLightSource(action0)
it.registerLightSource(action1)
it.registerLightSource(action2)
@@ -81,7 +87,33 @@ abstract class MediaViewHolder constructor(itemView: View) {
}
}
- abstract fun getAction(id: Int): ImageButton
+ fun getAction(id: Int): ImageButton {
+ return when (id) {
+ R.id.actionPlayPause -> actionPlayPause
+ R.id.actionNext -> actionNext
+ R.id.actionPrev -> actionPrev
+ R.id.action0 -> action0
+ R.id.action1 -> action1
+ R.id.action2 -> action2
+ R.id.action3 -> action3
+ R.id.action4 -> action4
+ else -> {
+ throw IllegalArgumentException()
+ }
+ }
+ }
+
+ fun getTransparentActionButtons(): List<ImageButton> {
+ return listOf(
+ actionNext,
+ actionPrev,
+ action0,
+ action1,
+ action2,
+ action3,
+ action4
+ )
+ }
fun marquee(start: Boolean, delay: Long) {
val longPressTextHandler = longPressText.getHandler()
@@ -91,4 +123,52 @@ abstract class MediaViewHolder constructor(itemView: View) {
}
longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
}
+
+ companion object {
+ /**
+ * Creates a MediaViewHolder.
+ *
+ * @param inflater LayoutInflater to use to inflate the layout.
+ * @param parent Parent of inflated view.
+ */
+ @JvmStatic fun create(
+ inflater: LayoutInflater,
+ parent: ViewGroup
+ ): MediaViewHolder {
+ val mediaView = inflater.inflate(R.layout.media_session_view, parent, false)
+ mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ // Because this media view (a TransitionLayout) is used to measure and layout the views
+ // in various states before being attached to its parent, we can't depend on the default
+ // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
+ mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+ return MediaViewHolder(mediaView).apply {
+ // Media playback is in the direction of tape, not time, so it stays LTR
+ seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
+ }
+ }
+
+ val controlsIds = setOf(
+ R.id.icon,
+ R.id.app_name,
+ R.id.header_title,
+ R.id.header_artist,
+ R.id.media_seamless,
+ R.id.media_progress_bar,
+ R.id.actionPlayPause,
+ R.id.actionNext,
+ R.id.actionPrev,
+ R.id.action0,
+ R.id.action1,
+ R.id.action2,
+ R.id.action3,
+ R.id.action4,
+ R.id.icon
+ )
+ val gutsIds = setOf(
+ R.id.remove_text,
+ R.id.cancel,
+ R.id.dismiss,
+ R.id.settings
+ )
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerSessionViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerSessionViewHolder.kt
deleted file mode 100644
index 6928ebb8bb32..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerSessionViewHolder.kt
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageButton
-import com.android.systemui.R
-
-/**
- * ViewHolder for a media player with MediaSession-based controls
- */
-class PlayerSessionViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
-
- // Action Buttons
- val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause)
- val actionNext = itemView.requireViewById<ImageButton>(R.id.actionNext)
- val actionPrev = itemView.requireViewById<ImageButton>(R.id.actionPrev)
-
- init {
- (player.background as IlluminationDrawable).let {
- it.registerLightSource(actionPlayPause)
- it.registerLightSource(actionNext)
- it.registerLightSource(actionPrev)
- }
- }
-
- override fun getAction(id: Int): ImageButton {
- return when (id) {
- R.id.actionPlayPause -> actionPlayPause
- R.id.actionNext -> actionNext
- R.id.actionPrev -> actionPrev
- R.id.action0 -> action0
- R.id.action1 -> action1
- R.id.action2 -> action2
- R.id.action3 -> action3
- R.id.action4 -> action4
- else -> {
- throw IllegalArgumentException()
- }
- }
- }
-
- companion object {
- /**
- * Creates a PlayerSessionViewHolder.
- *
- * @param inflater LayoutInflater to use to inflate the layout.
- * @param parent Parent of inflated view.
- */
- @JvmStatic fun create(
- inflater: LayoutInflater,
- parent: ViewGroup
- ): PlayerSessionViewHolder {
- val mediaView = inflater.inflate(R.layout.media_session_view, parent, false)
- mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
- // Because this media view (a TransitionLayout) is used to measure and layout the views
- // in various states before being attached to its parent, we can't depend on the default
- // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
- mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
- return PlayerSessionViewHolder(mediaView).apply {
- // Media playback is in the direction of tape, not time, so it stays LTR
- seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
- }
- }
-
- val controlsIds = setOf(
- R.id.icon,
- R.id.app_name,
- R.id.header_title,
- R.id.header_artist,
- R.id.media_seamless,
- R.id.media_progress_bar,
- R.id.actionPlayPause,
- R.id.actionNext,
- R.id.actionPrev,
- R.id.action0,
- R.id.action1,
- R.id.action2,
- R.id.action3,
- R.id.action4,
- R.id.icon
- )
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
- )
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
deleted file mode 100644
index dd3fa89dea66..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageButton
-import android.widget.TextView
-import com.android.systemui.R
-
-/**
- * ViewHolder for a media player.
- */
-class PlayerViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
-
- // Seek bar
- val progressTimes = itemView.requireViewById<ViewGroup>(R.id.notification_media_progress_time)
- override val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time)
- override val totalTimeView = itemView.requireViewById<TextView>(R.id.media_total_time)
-
- override fun getAction(id: Int): ImageButton {
- return when (id) {
- R.id.action0 -> action0
- R.id.action1 -> action1
- R.id.action2 -> action2
- R.id.action3 -> action3
- R.id.action4 -> action4
- else -> {
- throw IllegalArgumentException()
- }
- }
- }
-
- companion object {
- /**
- * Creates a PlayerViewHolder.
- *
- * @param inflater LayoutInflater to use to inflate the layout.
- * @param parent Parent of inflated view.
- */
- @JvmStatic fun create(inflater: LayoutInflater, parent: ViewGroup): PlayerViewHolder {
- val mediaView = inflater.inflate(R.layout.media_view, parent, false)
- mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
- // Because this media view (a TransitionLayout) is used to measure and layout the views
- // in various states before being attached to its parent, we can't depend on the default
- // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
- mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
- return PlayerViewHolder(mediaView).apply {
- // Media playback is in the direction of tape, not time, so it stays LTR
- seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
- progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR
- }
- }
-
- val controlsIds = setOf(
- R.id.icon,
- R.id.app_name,
- R.id.album_art,
- R.id.header_title,
- R.id.header_artist,
- R.id.media_seamless,
- R.id.notification_media_progress_time,
- R.id.media_progress_bar,
- R.id.action0,
- R.id.action1,
- R.id.action2,
- R.id.action3,
- R.id.action4,
- R.id.icon
- )
- val gutsIds = setOf(
- R.id.remove_text,
- R.id.cancel,
- R.id.dismiss,
- R.id.settings
- )
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index 57701ab618c9..e5b41b1a6771 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -27,28 +27,17 @@ import com.android.systemui.R
* <p>Updates the seek bar views in response to changes to the model.
*/
class SeekBarObserver(
- private val holder: MediaViewHolder,
- private val useSessionLayout: Boolean
+ private val holder: MediaViewHolder
) : Observer<SeekBarViewModel.Progress> {
val seekBarEnabledMaxHeight = holder.seekBar.context.resources
.getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height)
val seekBarDisabledHeight = holder.seekBar.context.resources
.getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height)
- val seekBarEnabledVerticalPadding = if (useSessionLayout) {
- holder.seekBar.context.resources
+ val seekBarEnabledVerticalPadding = holder.seekBar.context.resources
.getDimensionPixelSize(R.dimen.qs_media_session_enabled_seekbar_vertical_padding)
- } else {
- holder.seekBar.context.resources
- .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding)
- }
- val seekBarDisabledVerticalPadding = if (useSessionLayout) {
- holder.seekBar.context.resources
+ val seekBarDisabledVerticalPadding = holder.seekBar.context.resources
.getDimensionPixelSize(R.dimen.qs_media_session_disabled_seekbar_vertical_padding)
- } else {
- holder.seekBar.context.resources
- .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding)
- }
init {
val seekBarProgressWavelength = holder.seekBar.context.resources
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index a6464829623f..f4fcf10e7bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -169,12 +169,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
setSingleLineLayout(getItemTitle(device), true /* bFocused */,
true /* showSeekBar */,
false /* showProgressBar */, false /* showStatus */);
- mCheckBox.setOnCheckedChangeListener(null);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(true);
- mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
- onCheckBoxClicked(false, device);
- });
+ mSeekBar.setOnClickListener(null);
+ mSeekBar.setOnClickListener(v -> onGroupActionTriggered(false, device));
setCheckBoxColor(mCheckBox, mController.getColorItemContent());
initSeekbar(device);
} else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
@@ -188,17 +186,13 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
initSeekbar(device);
mCurrentActivePosition = position;
} else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
- mCheckBox.setOnCheckedChangeListener(null);
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(false);
- mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
- onCheckBoxClicked(true, device);
- });
+ mContainerLayout.setOnClickListener(v -> onGroupActionTriggered(true, device));
setCheckBoxColor(mCheckBox, mController.getColorItemContent());
setSingleLineLayout(getItemTitle(device), false /* bFocused */,
false /* showSeekBar */,
false /* showProgressBar */, false /* showStatus */);
- mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -228,7 +222,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
}
- private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
+ private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
mController.addDeviceToPlayMedia(device);
} else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index ea7f7f2bb646..1cc96c0fd0de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -637,7 +637,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
}
private boolean isPlayBackInfoLocal() {
- return mMediaController.getPlaybackInfo() != null
+ return mMediaController != null
+ && mMediaController.getPlaybackInfo() != null
&& mMediaController.getPlaybackInfo().getPlaybackType()
== MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
new file mode 100644
index 000000000000..0a3c5bf24b8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java
@@ -0,0 +1,68 @@
+/*
+ * 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.media.dialog;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.SeekBar;
+
+/**
+ * Customized seekbar used by MediaOutputDialog, which only changes progress when dragging,
+ * otherwise performs click.
+ */
+public class MediaOutputSeekbar extends SeekBar {
+ private static final int DRAGGING_THRESHOLD = 20;
+ private boolean mIsDragging = false;
+
+ public MediaOutputSeekbar(Context context) {
+ super(context);
+ }
+
+ public MediaOutputSeekbar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int width = getWidth()
+ - getPaddingLeft()
+ - getPaddingRight();
+ int thumbPos = getPaddingLeft()
+ + width
+ * getProgress()
+ / getMax();
+ if (event.getAction() == MotionEvent.ACTION_DOWN
+ && Math.abs(event.getX() - thumbPos) < DRAGGING_THRESHOLD) {
+ mIsDragging = true;
+ super.onTouchEvent(event);
+ } else if (event.getAction() == MotionEvent.ACTION_MOVE && mIsDragging) {
+ super.onTouchEvent(event);
+ } else if (event.getAction() == MotionEvent.ACTION_UP && mIsDragging) {
+ mIsDragging = false;
+ super.onTouchEvent(event);
+ } else if (event.getAction() == MotionEvent.ACTION_UP && !mIsDragging) {
+ performClick();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean performClick() {
+ return super.performClick();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d8d86779ea0a..fab19d62b70d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -86,7 +86,6 @@ import android.view.HapticFeedbackConstants;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
@@ -98,6 +97,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
@@ -111,8 +111,10 @@ import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.DeadZone;
@@ -140,7 +142,10 @@ import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.ViewController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -149,15 +154,15 @@ import java.util.Locale;
import java.util.Optional;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
import dagger.Lazy;
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedFactory;
-import dagger.assisted.AssistedInject;
/**
* Contains logic for a navigation bar view.
*/
-public class NavigationBar implements View.OnAttachStateChangeListener, Callbacks {
+@NavigationBarScope
+public class NavigationBar extends ViewController<NavigationBarView> implements Callbacks {
public static final String TAG = "NavigationBar";
private static final boolean DEBUG = false;
@@ -172,12 +177,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private static final long AUTODIM_TIMEOUT_MS = 2250;
private final Context mContext;
+ private final Bundle mSavedState;
private final WindowManager mWindowManager;
private final AccessibilityManager mAccessibilityManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final SysUiState mSysUiFlagsContainer;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final ShadeController mShadeController;
@@ -188,15 +195,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private final CommandQueue mCommandQueue;
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
+ private final DeviceConfigProxy mDeviceConfigProxy;
private final Optional<BackAnimation> mBackAnimation;
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
-
- private Bundle mSavedState;
- private NavigationBarView mNavigationBarView;
private NavigationBarFrame mFrame;
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -256,7 +261,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private int mCurrentRotation;
private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener;
private boolean mShowOrientedHandleForImmersiveMode;
- private DeadZone mDeadZone;
+ private final DeadZone mDeadZone;
private boolean mImeVisible;
@com.android.internal.annotations.VisibleForTesting
@@ -311,7 +316,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// TODO(b/198002034): Content observers currently can still be called back after
// being unregistered, and in this case we can ignore the change if the nav bar
// has been destroyed already
- if (mNavigationBarView == null) {
+ if (mView == null) {
return;
}
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
@@ -322,14 +327,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
- mNavigationBarView.updateStates();
+ mView.updateStates();
updateScreenPinningGestures();
}
@Override
public void onQuickStepStarted() {
// Use navbar dragging as a signal to hide the rotate button
- mNavigationBarView.getRotationButtonController().setRotateSuggestionButtonState(false);
+ mView.getRotationButtonController().setRotateSuggestionButtonState(false);
// Hide the notifications panel when quick step starts
mShadeController.collapsePanel(true /* animate */);
@@ -366,12 +371,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// Disallow home handle animations when in gestural
animate = false;
forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
- buttonDispatcher = mNavigationBarView.getHomeHandle();
+ buttonDispatcher = mView.getHomeHandle();
if (getBarTransitions() != null) {
getBarTransitions().setBackgroundOverrideAlpha(alpha);
}
} else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mNavigationBarView.getBackButton();
+ buttonDispatcher = mView.getBackButton();
}
if (buttonDispatcher != null) {
buttonDispatcher.setVisibility(
@@ -382,7 +387,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
@Override
public void onHomeRotationEnabled(boolean enabled) {
- mNavigationBarView.getRotationButtonController().setHomeRotationEnabled(enabled);
+ mView.getRotationButtonController().setHomeRotationEnabled(enabled);
}
@Override
@@ -390,20 +395,18 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// If the overview has fixed orientation that may change display to natural rotation,
// we don't want the user rotation to be reset. So after user returns to application,
// it can keep in the original rotation.
- mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+ mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
}
@Override
public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
- mNavigationBarView
- .getFloatingRotationButton()
- .onTaskbarStateChanged(visible, stashed);
+ mView.getFloatingRotationButton().onTaskbarStateChanged(visible, stashed);
}
@Override
public void onToggleRecentApps() {
// The same case as onOverviewShown but only for 3-button navigation.
- mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+ mView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
}
};
@@ -416,11 +419,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
};
private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
- private final Runnable mEnableLayoutTransitions = () ->
- mNavigationBarView.setLayoutTransitionsEnabled(true);
+ private final Runnable mEnableLayoutTransitions = () -> mView.setLayoutTransitionsEnabled(true);
private final Runnable mOnVariableDurationHomeLongClick = () -> {
- if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
- mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
+ if (onHomeLongClick(mView.getHomeButton().getCurrentView())) {
+ mView.getHomeButton().getCurrentView().performHapticFeedback(
HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
@@ -439,7 +441,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mHomeButtonLongPressDurationMs = Optional.of(
properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
).filter(duration -> duration != 0);
- if (mNavigationBarView != null) {
+ if (mView != null) {
reconfigureHomeLongClick();
}
}
@@ -469,14 +471,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
return;
}
mHasBlurs = hasBlurs;
- mNavigationBarView.setWindowHasBlurs(hasBlurs);
+ mView.setWindowHasBlurs(hasBlurs);
}
};
- @AssistedInject
+ @Inject
NavigationBar(
- @Assisted Context context,
- @Assisted WindowManager windowManager,
+ NavigationBarView navigationBarView,
+ NavigationBarFrame navigationBarFrame,
+ @Nullable Bundle savedState,
+ @DisplayId Context context,
+ @DisplayId WindowManager windowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -484,6 +489,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
OverviewProxyService overviewProxyService,
NavigationModeController navigationModeController,
StatusBarStateController statusBarStateController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
SysUiState sysUiFlagsContainer,
BroadcastDispatcher broadcastDispatcher,
CommandQueue commandQueue,
@@ -503,14 +509,20 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
AutoHideController.Factory autoHideControllerFactory,
Optional<TelecomManager> telecomManagerOptional,
InputMethodManager inputMethodManager,
+ DeadZone deadZone,
+ DeviceConfigProxy deviceConfigProxy,
Optional<BackAnimation> backAnimation) {
+ super(navigationBarView);
+ mFrame = navigationBarFrame;
mContext = context;
+ mSavedState = savedState;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mSysUiFlagsContainer = sysUiFlagsContainer;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeController = shadeController;
@@ -521,6 +533,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mCommandQueue = commandQueue;
mPipOptional = pipOptional;
mRecentsOptional = recentsOptional;
+ mDeadZone = deadZone;
+ mDeviceConfigProxy = deviceConfigProxy;
mBackAnimation = backAnimation;
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
@@ -538,25 +552,23 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
public NavigationBarView getView() {
- return mNavigationBarView;
+ return mView;
}
- public View createView(Bundle savedState, boolean initialVisibility) {
- mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
- R.layout.navigation_bar_window, null);
- View barView = LayoutInflater.from(mFrame.getContext()).inflate(
- R.layout.navigation_bar, mFrame);
- barView.addOnAttachStateChangeListener(this);
- mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
- mDeadZone = new DeadZone(mNavigationBarView);
- mNavigationBarView.setTouchHandler(mTouchHandler);
- mNavigationBarView.setNavBarMode(mNavBarMode);
+ @Override
+ public void onInit() {
+ // TODO: A great deal of this code should probalby live in onViewAttached.
+ // It should also has corresponding cleanup in onViewDetached.
+ mView.setTouchHandler(mTouchHandler);
+ mView.setNavBarMode(mNavBarMode);
- mNavigationBarView.updateRotationButton();
+ mView.updateRotationButton();
- mNavigationBarView.setVisibility(initialVisibility ? View.VISIBLE : View.INVISIBLE);
+ mView.setVisibility(
+ mStatusBarKeyguardViewManager.isNavBarVisible() ? View.VISIBLE : View.INVISIBLE);
+
+ if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
- if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
mWindowManager.addView(mFrame,
getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
.getRotation()));
@@ -568,26 +580,25 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mNavBarHelper.init();
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
- mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
+ mForceNavBarHandleOpaque = mDeviceConfigProxy.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
/* defaultValue = */ true);
- mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
+ mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
HOME_BUTTON_LONG_PRESS_DURATION_MS,
/* defaultValue = */ 0
)).filter(duration -> duration != 0);
- DeviceConfig.addOnPropertiesChangedListener(
+ mDeviceConfigProxy.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- if (savedState != null) {
- mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
- mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0);
- mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0);
- mBehavior = savedState.getInt(EXTRA_BEHAVIOR, 0);
- mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false);
+ if (mSavedState != null) {
+ mDisabledFlags1 = mSavedState.getInt(EXTRA_DISABLE_STATE, 0);
+ mDisabledFlags2 = mSavedState.getInt(EXTRA_DISABLE2_STATE, 0);
+ mAppearance = mSavedState.getInt(EXTRA_APPEARANCE, 0);
+ mBehavior = mSavedState.getInt(EXTRA_BEHAVIOR, 0);
+ mTransientShown = mSavedState.getBoolean(EXTRA_TRANSIENT_STATE, false);
}
- mSavedState = savedState;
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
@@ -595,14 +606,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
-
- return barView;
}
public void destroyView() {
setAutoHideController(/* autoHideController */ null);
mCommandQueue.removeCallback(this);
- mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
+ mWindowManager.removeViewImmediate(mView.getRootView());
mNavigationModeController.removeListener(mModeChangedListener);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -610,29 +619,29 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
- DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
}
@Override
- public void onViewAttachedToWindow(View v) {
- final Display display = v.getDisplay();
- mNavigationBarView.setComponents(mRecentsOptional);
- mNavigationBarView.setComponents(mCentralSurfacesOptionalLazy.get().get().getPanelController());
- mNavigationBarView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer);
- mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
- mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
+ public void onViewAttached() {
+ final Display display = mView.getDisplay();
+ mView.setComponents(mRecentsOptional);
+ mView.setComponents(mCentralSurfacesOptionalLazy.get().get().getPanelController());
+ mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer);
+ mView.setOnVerticalChangedListener(this::onVerticalChanged);
+ mView.setOnTouchListener(this::onNavigationTouch);
if (mSavedState != null) {
- mNavigationBarView.getLightTransitionsController().restoreState(mSavedState);
+ mView.getLightTransitionsController().restoreState(mSavedState);
}
setNavigationIconHints(mNavigationIconHints);
- mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
- mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarView.setNavBarMode(mNavBarMode);
+ mView.setWindowVisible(isNavBarWindowVisible());
+ mView.setBehavior(mBehavior);
+ mView.setNavBarMode(mNavBarMode);
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
- mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
- mBackAnimation.ifPresent(mNavigationBarView::registerBackAnimation);
+ mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener);
+ mBackAnimation.ifPresent(mView::registerBackAnimation);
prepareNavigationBarView();
checkNavBarModes();
@@ -650,7 +659,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// Currently there is no accelerometer sensor on non-default display.
if (mIsOnDefaultDisplay) {
final RotationButtonController rotationButtonController =
- mNavigationBarView.getRotationButtonController();
+ mView.getRotationButtonController();
rotationButtonController.setRotationCallback(mRotationWatcher);
// Reset user rotation pref to match that of the WindowManager if starting in locked
@@ -684,12 +693,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
@Override
- public void onViewDetachedFromWindow(View v) {
+ public void onViewDetached() {
final RotationButtonController rotationButtonController =
- mNavigationBarView.getRotationButtonController();
+ mView.getRotationButtonController();
rotationButtonController.setRotationCallback(null);
- mNavigationBarView.getBarTransitions().destroy();
- mNavigationBarView.getLightTransitionsController().destroy(mContext);
+ mView.getBarTransitions().destroy();
+ mView.getLightTransitionsController().destroy(mContext);
mOverviewProxyService.removeCallback(mOverviewProxyListener);
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
if (mOrientationHandle != null) {
@@ -703,9 +712,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
- mPipOptional.ifPresent(mNavigationBarView::removePipExclusionBoundsChangeListener);
+ mPipOptional.ifPresent(mView::removePipExclusionBoundsChangeListener);
mFrame = null;
- mNavigationBarView = null;
mOrientationHandle = null;
}
@@ -716,7 +724,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
outState.putInt(EXTRA_APPEARANCE, mAppearance);
outState.putInt(EXTRA_BEHAVIOR, mBehavior);
outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
- mNavigationBarView.getLightTransitionsController().saveState(outState);
+ mView.getLightTransitionsController().saveState(outState);
}
/**
@@ -779,7 +787,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mOrientationHandle.mapRectFromViewToScreenCoords(boundsOnScreen, true);
Rect boundsRounded = new Rect();
boundsOnScreen.roundOut(boundsRounded);
- mNavigationBarView.setOrientedHandleSamplingRegion(boundsRounded);
+ mView.setOrientedHandleSamplingRegion(boundsRounded);
};
mOrientationHandle.getViewTreeObserver().addOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
@@ -808,7 +816,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
case Surface.ROTATION_90:
case Surface.ROTATION_270:
height = dispSize.height();
- width = mNavigationBarView.getHeight();
+ width = mView.getHeight();
break;
case Surface.ROTATION_180:
case Surface.ROTATION_0:
@@ -818,7 +826,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
return;
}
width = dispSize.width();
- height = mNavigationBarView.getHeight();
+ height = mView.getHeight();
break;
}
@@ -828,7 +836,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mOrientationParams.height = height;
mOrientationParams.width = width;
mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
- mNavigationBarView.setVisibility(View.GONE);
+ mView.setVisibility(View.GONE);
mOrientationHandle.setVisibility(View.VISIBLE);
}
}
@@ -839,22 +847,22 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// mOrientedHandle is initialized lazily
mOrientationHandle.setVisibility(View.GONE);
}
- mNavigationBarView.setVisibility(View.VISIBLE);
- mNavigationBarView.setOrientedHandleSamplingRegion(null);
+ mView.setVisibility(View.VISIBLE);
+ mView.setOrientedHandleSamplingRegion(null);
}
private void reconfigureHomeLongClick() {
- if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
+ if (mView.getHomeButton().getCurrentView() == null) {
return;
}
if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
- mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
- mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
- mNavigationBarView.getHomeButton().setOnLongClickListener(null);
+ mView.getHomeButton().getCurrentView().setLongClickable(false);
+ mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
+ mView.getHomeButton().setOnLongClickListener(null);
} else {
- mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true);
- mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
- mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
+ mView.getHomeButton().getCurrentView().setLongClickable(true);
+ mView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true);
+ mView.getHomeButton().setOnLongClickListener(this::onHomeLongClick);
}
}
@@ -877,8 +885,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
pw.println(" mTransientShown=" + mTransientShown);
pw.println(" mTransientShownFromGestureOnSystemBar="
+ mTransientShownFromGestureOnSystemBar);
- dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
- mNavigationBarView.dump(pw);
+ dumpBarTransitions(pw, "mNavigationBarView", mView.getBarTransitions());
+ mView.dump(pw);
}
// ----- CommandQueue Callbacks -----
@@ -914,7 +922,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
orientSecondaryHomeHandle();
}
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
- mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ mView.setWindowVisible(isNavBarWindowVisible());
}
}
@@ -923,12 +931,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
// The CommandQueue callbacks are added when the view is created to ensure we track other
// states, but until the view is attached (at the next traversal), the view's display is
// not valid. Just ignore the rotation in this case.
- if (!mNavigationBarView.isAttachedToWindow()) return;
+ if (!mView.isAttachedToWindow()) return;
final boolean rotateSuggestionsDisabled = RotationButtonController
.hasDisable2RotateSuggestionFlag(mDisabledFlags2);
final RotationButtonController rotationButtonController =
- mNavigationBarView.getRotationButtonController();
+ mView.getRotationButtonController();
final RotationButton rotationButton = rotationButtonController.getRotationButton();
if (RotationContextButton.DEBUG_ROTATION) {
@@ -950,7 +958,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
if (running) {
mNavbarOverlayController.setButtonState(/* visible */false, /* force */true);
}
- mNavigationBarView.getRotationButtonController().setRecentsAnimationRunning(running);
+ mView.getRotationButtonController().setRecentsAnimationRunning(running);
}
/** Restores the appearance and the transient saved state to {@link NavigationBar}. */
@@ -985,7 +993,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
if (mBehavior != behavior) {
mBehavior = behavior;
- mNavigationBarView.setBehavior(behavior);
+ mView.setBehavior(behavior);
updateSystemUiStateFlags();
}
}
@@ -1026,7 +1034,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
private void handleTransientChanged() {
- mNavigationBarView.onTransientStateChanged(mTransientShown,
+ mView.onTransientStateChanged(mTransientShown,
mTransientShownFromGestureOnSystemBar);
final int transitionMode = transitionMode(mTransientShown, mAppearance);
if (updateTransitionMode(transitionMode) && mLightBarController != null) {
@@ -1076,7 +1084,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
| StatusBarManager.DISABLE_SEARCH);
if (masked != mDisabledFlags1) {
mDisabledFlags1 = masked;
- mNavigationBarView.setDisabledFlags(state1, mSysUiFlagsContainer);
+ mView.setDisabledFlags(state1, mSysUiFlagsContainer);
updateScreenPinningGestures();
}
@@ -1092,13 +1100,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private void setDisabled2Flags(int state2) {
// Method only called on change of disable2 flags
- mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2);
+ mView.getRotationButtonController().onDisable2FlagChanged(state2);
}
// ----- Internal stuff -----
private void refreshLayout(int layoutDirection) {
- mNavigationBarView.setLayoutDirection(layoutDirection);
+ mView.setLayoutDirection(layoutDirection);
}
private boolean shouldDisableNavbarGestures() {
@@ -1107,7 +1115,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
private void repositionNavigationBar(int rotation) {
- if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
+ if (mView == null || !mView.isAttachedToWindow()) return;
prepareNavigationBarView();
@@ -1117,10 +1125,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
private void updateScreenPinningGestures() {
// Change the cancel pin gesture to home and back if recents button is invisible
boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
- ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
+ ButtonDispatcher backButton = mView.getBackButton();
+ ButtonDispatcher recentsButton = mView.getRecentsButton();
if (pinningActive) {
- boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
+ boolean recentsVisible = mView.isRecentsButtonVisible();
backButton.setOnLongClickListener(recentsVisible
? this::onLongPressBackRecents
: this::onLongPressBackHome);
@@ -1135,27 +1143,27 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
private void notifyNavigationBarScreenOn() {
- mNavigationBarView.updateNavButtonIcons();
+ mView.updateNavButtonIcons();
}
private void prepareNavigationBarView() {
- mNavigationBarView.reorient();
+ mView.reorient();
- ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
+ ButtonDispatcher recentsButton = mView.getRecentsButton();
recentsButton.setOnClickListener(this::onRecentsClick);
recentsButton.setOnTouchListener(this::onRecentsTouch);
- ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
+ ButtonDispatcher homeButton = mView.getHomeButton();
homeButton.setOnTouchListener(this::onHomeTouch);
reconfigureHomeLongClick();
- ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
+ ButtonDispatcher accessibilityButton = mView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
updateAccessibilityStateFlags();
- ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
+ ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
updateScreenPinningGestures();
@@ -1212,7 +1220,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
@VisibleForTesting
boolean onHomeLongClick(View v) {
- if (!mNavigationBarView.isRecentsButtonVisible()
+ if (!mView.isRecentsButtonVisible()
&& ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
return onLongPressBackHome(v);
}
@@ -1227,7 +1235,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mAssistManagerLazy.get().startAssist(args);
mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
- mNavigationBarView.abortCurrentGesture();
+ mView.abortCurrentGesture();
return true;
}
@@ -1302,8 +1310,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
return true;
} else if (v.getId() == btnId1) {
ButtonDispatcher button = btnId2 == R.id.recent_apps
- ? mNavigationBarView.getRecentsButton()
- : mNavigationBarView.getHomeButton();
+ ? mView.getRecentsButton() : mView.getHomeButton();
if (!button.getCurrentView().isPressed()) {
// If we aren't pressing recents/home right now then they presses
// won't be together, so send the standard long-press action.
@@ -1323,15 +1330,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
} else if (v.getId() == btnId2) {
return btnId2 == R.id.recent_apps
? false
- : onHomeLongClick(
- mNavigationBarView.getHomeButton().getCurrentView());
+ : onHomeLongClick(mView.getHomeButton().getCurrentView());
}
}
} finally {
if (stopLockTaskMode) {
activityManager.stopSystemLockTaskMode();
// When exiting refresh disabled flags.
- mNavigationBarView.updateNavButtonIcons();
+ mView.updateNavButtonIcons();
}
}
@@ -1363,11 +1369,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
void updateAccessibilityStateFlags() {
- if (mNavigationBarView != null) {
+ if (mView != null) {
int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
- mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
+ mView.setAccessibilityButtonState(clickable, longClickable);
}
updateSystemUiStateFlags();
}
@@ -1416,7 +1422,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mLightBarController = lightBarController;
if (mLightBarController != null) {
mLightBarController.setNavigationBar(
- mNavigationBarView.getLightTransitionsController());
+ mView.getLightTransitionsController());
}
}
@@ -1426,7 +1432,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
if (mAutoHideController != null) {
mAutoHideController.setNavigationBar(mAutoHideUiElement);
}
- mNavigationBarView.setAutoHideController(autoHideController);
+ mView.setAutoHideController(autoHideController);
}
private boolean isTransientShown() {
@@ -1458,11 +1464,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
.orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
- mNavigationBarView.getBarTransitions().transitionTo(mTransitionMode, anim);
+ mView.getBarTransitions().transitionTo(mTransitionMode, anim);
}
public void disableAnimationsDuringHide(long delay) {
- mNavigationBarView.setLayoutTransitionsEnabled(false);
+ mView.setLayoutTransitionsEnabled(false);
mHandler.postDelayed(mEnableLayoutTransitions,
delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
}
@@ -1478,11 +1484,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
public NavigationBarTransitions getBarTransitions() {
- return mNavigationBarView.getBarTransitions();
+ return mView.getBarTransitions();
}
public void finishBarAnimations() {
- mNavigationBarView.getBarTransitions().finishAnimations();
+ mView.getBarTransitions().finishAnimations();
}
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
@@ -1567,8 +1573,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
- if (mNavigationBarView != null
- && mNavigationBarView.needsReorient(rotation)) {
+ if (mView != null && mView.needsReorient(rotation)) {
repositionNavigationBar(rotation);
}
};
@@ -1578,14 +1583,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
public void onReceive(Context context, Intent intent) {
// TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is
// async, but we've already cleared the fields. Just return early in this case.
- if (mNavigationBarView == null) {
+ if (mView == null) {
return;
}
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn();
- mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
+ mView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
@@ -1607,11 +1612,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
final boolean oldBackAlt =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
if (newBackAlt != oldBackAlt) {
- mNavigationBarView.onImeVisibilityChanged(newBackAlt);
+ mView.onImeVisibilityChanged(newBackAlt);
mImeVisible = newBackAlt;
}
- mNavigationBarView.setNavigationIconHints(hints);
+ mView.setNavigationIconHints(hints);
}
if (DEBUG) {
android.widget.Toast.makeText(mContext,
@@ -1637,8 +1642,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
}
- if (mNavigationBarView != null) {
- mNavigationBarView.setNavBarMode(mode);
+ if (mView != null) {
+ mView.setNavBarMode(mode);
}
}
};
@@ -1671,14 +1676,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
switch (action) {
case MotionEvent.ACTION_DOWN:
// Allow gestures starting in the deadzone to be slippery
- mNavigationBarView.setSlippery(true);
+ mView.setSlippery(true);
mDeadZoneConsuming = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// When a gesture started in the deadzone is finished, restore
// slippery state
- mNavigationBarView.updateSlippery();
+ mView.updateSlippery();
mDeadZoneConsuming = false;
break;
}
@@ -1687,14 +1692,4 @@ public class NavigationBar implements View.OnAttachStateChangeListener, Callback
return false;
}
};
-
-
- /**
- * Injectable factory for construction a {@link NavigationBar}.
- */
- @AssistedFactory
- public interface Factory {
- /** Construct a {@link NavigationBar} */
- NavigationBar create(Context context, WindowManager windowManager);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarComponent.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarComponent.java
new file mode 100644
index 000000000000..1d792af8ab59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarComponent.java
@@ -0,0 +1,63 @@
+/*
+ * 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.navigationbar;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.dagger.qualifiers.DisplayId;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for a NavigationBar.
+ *
+ * Generally creatd on a per-display basis.
+ */
+@Subcomponent(modules = { NavigationBarModule.class })
+@NavigationBarComponent.NavigationBarScope
+public interface NavigationBarComponent {
+
+ /** Factory for {@link NavigationBarComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ NavigationBarComponent create(
+ @BindsInstance @DisplayId Context context,
+ @BindsInstance @Nullable Bundle savedState);
+ }
+
+ /** */
+ NavigationBar getNavigationBar();
+
+ /**
+ * Scope annotation for singleton items within the NavigationBarComponent.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface NavigationBarScope {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b640e1d24385..ade86ae76db4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -39,7 +39,6 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
@@ -84,7 +83,7 @@ public class NavigationBarController implements
private final Context mContext;
private final Handler mHandler;
- private final NavigationBar.Factory mNavigationBarFactory;
+ private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
private final DisplayManager mDisplayManager;
private final TaskbarDelegate mTaskbarDelegate;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -110,7 +109,7 @@ public class NavigationBarController implements
ConfigurationController configurationController,
NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
- NavigationBar.Factory navigationBarFactory,
+ NavigationBarComponent.Factory navigationBarComponentFactory,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
DumpManager dumpManager,
AutoHideController autoHideController,
@@ -119,7 +118,7 @@ public class NavigationBarController implements
Optional<BackAnimation> backAnimation) {
mContext = context;
mHandler = mainHandler;
- mNavigationBarFactory = navigationBarFactory;
+ mNavigationBarComponentFactory = navigationBarComponentFactory;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -324,14 +323,13 @@ public class NavigationBarController implements
final Context context = isOnDefaultDisplay
? mContext
: mContext.createDisplayContext(display);
- NavigationBar navBar = mNavigationBarFactory.create(
- context, context.getSystemService(WindowManager.class));
-
+ NavigationBarComponent component = mNavigationBarComponentFactory.create(
+ context, savedState);
+ NavigationBar navBar = component.getNavigationBar();
+ navBar.init();
mNavigationBars.put(displayId, navBar);
- boolean navBarVisible = mStatusBarKeyguardViewManager.isNavBarVisible();
- View navigationBarView = navBar.createView(savedState, navBarVisible);
- navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ navBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (result != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
new file mode 100644
index 000000000000..93bf136e88f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.navigationbar;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Module for {@link com.android.systemui.navigationbar.NavigationBarComponent}. */
+@Module
+public interface NavigationBarModule {
+ /** A Layout inflater specific to the display's context. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static LayoutInflater provideLayoutInflater(@DisplayId Context context) {
+ return LayoutInflater.from(context);
+ }
+
+ /** */
+ @Provides
+ @NavigationBarScope
+ static NavigationBarFrame provideNavigationBarFrame(@DisplayId LayoutInflater layoutInflater) {
+ return (NavigationBarFrame) layoutInflater.inflate(R.layout.navigation_bar_window, null);
+ }
+
+ /** */
+ @Provides
+ @NavigationBarScope
+ static NavigationBarView provideNavigationBarview(
+ @DisplayId LayoutInflater layoutInflater, NavigationBarFrame frame) {
+ View barView = layoutInflater.inflate(R.layout.navigation_bar, frame);
+ return barView.findViewById(R.id.navigation_bar_view);
+ }
+
+ /** A WindowManager specific to the display's context. */
+ @Provides
+ @NavigationBarScope
+ @DisplayId
+ static WindowManager provideWindowManager(@DisplayId Context context) {
+ return context.getSystemService(WindowManager.class);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
index 7fb58f0d8fc6..9305d0518732 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
@@ -30,6 +30,8 @@ import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
+import javax.inject.Inject;
+
/**
* The "dead zone" consumes unintentional taps along the top edge of the navigation bar.
* When users are typing quickly on an IME they may attempt to hit the space bar, overshoot, and
@@ -82,6 +84,7 @@ public class DeadZone {
}
};
+ @Inject
public DeadZone(NavigationBarView view) {
mNavigationBarView = view;
mNavBarController = Dependency.get(NavigationBarController.class);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 2ac34b22be5b..369a552a934a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -271,6 +271,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mPlaySound = false;
return;
}
+ if (!showLowBatteryNotification()) {
+ return;
+ }
final int warningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
@@ -323,6 +326,29 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
return isSevereState && mUseSevereDialog;
}
+ /**
+ * Disable low battery warning notification if battery saver schedule mode set as
+ * "Based on percentage".
+ *
+ * return {@code false} if scheduled by percentage.
+ */
+ private boolean showLowBatteryNotification() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final int mode = Settings.Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
+ PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
+
+ // Return true if battery saver schedule mode will not trigger by percentage.
+ if (mode != PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
+ return true;
+ }
+
+ // Return true if battery saver mode trigger percentage is less than 0, which means it is
+ // set as "Based on routine" mode, otherwise it will be "Based on percentage" mode.
+ final int threshold =
+ Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ return threshold <= 0;
+ }
+
private void showAutoSaverSuggestionNotification() {
final CharSequence message = mContext.getString(R.string.auto_saver_text);
final Notification.Builder nb =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 27da6f3d2d2c..842a1b92f690 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -247,18 +247,7 @@ internal class FooterActionsController @Inject constructor(
}
fun setExpansion(headerExpansionFraction: Float) {
- if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
- if (headerExpansionFraction != lastExpansion) {
- if (headerExpansionFraction >= 1f) {
- mView.animate().alpha(1f).setDuration(500L).start()
- } else if (lastExpansion >= 1f && headerExpansionFraction < 1f) {
- mView.animate().alpha(0f).setDuration(250L).start()
- }
- lastExpansion = headerExpansionFraction
- }
- } else {
- alphaAnimator.setPosition(headerExpansionFraction)
- }
+ alphaAnimator.setPosition(headerExpansionFraction)
}
fun setKeyguardShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index f8680552f90a..ceb895f74d90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -139,13 +139,17 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
}
void updateResources(QSPanelController qsPanelController,
- QuickStatusBarHeaderController quickStatusBarHeaderController) {
+ QuickStatusBarHeaderController quickStatusBarHeaderController,
+ boolean newFooter) {
+ int bottomPadding = 0;
+ if (newFooter) {
+ bottomPadding = getResources().getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
+ }
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
Utils.getQsHeaderSystemIconsAreaHeight(mContext),
mQSPanelContainer.getPaddingEnd(),
- mQSPanelContainer.getPaddingBottom()
- );
+ bottomPadding);
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
int padding = getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 7d61991c910a..61da18224023 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -18,6 +18,8 @@ package com.android.systemui.qs;
import android.content.res.Configuration;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -30,23 +32,26 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
private final QSPanelController mQsPanelController;
private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
private final ConfigurationController mConfigurationController;
+ private final boolean mNewFooter;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
- mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
+ mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController, mNewFooter);
}
};
@Inject
QSContainerImplController(QSContainerImpl view, QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FeatureFlags featureFlags) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
+ mNewFooter = featureFlags.isEnabled(Flags.NEW_FOOTER);
}
@Override
@@ -60,7 +65,7 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
@Override
protected void onViewAttached() {
- mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
+ mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController, mNewFooter);
mConfigurationController.addCallback(mConfigurationListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index fe8c309ad2f3..4e631046063b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -599,6 +599,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSPanelScrollView.getHeight());
}
mQSPanelScrollView.setClipBounds(mQsBounds);
+
+ mQsMediaHost.getCurrentClipping().set(0, 0, getView().getMeasuredWidth(),
+ mQSPanelScrollView.getMeasuredHeight() - mQSPanelScrollView.getPaddingBottom());
}
private void updateMediaPositions() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b04d75273831..11a36ada4e36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -374,7 +374,7 @@ public class QSPanel extends LinearLayout implements Tunable {
setPaddingRelative(getPaddingStart(),
paddingTop,
getPaddingEnd(),
- mUseNewFooter ? res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom) : 0);
+ getPaddingEnd());
}
void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 865f09337fa3..dd2929c9a67a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -33,7 +33,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostState;
@@ -46,7 +45,6 @@ import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.Utils;
import javax.inject.Inject;
import javax.inject.Named;
@@ -65,7 +63,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final FalsingManager mFalsingManager;
private final BrightnessController mBrightnessController;
private final BrightnessSliderController mBrightnessSliderController;
- private final MediaFlags mMediaFlags;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
private final FeatureFlags mFeatureFlags;
@@ -75,7 +72,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
new QSPanel.OnConfigurationChangedListener() {
@Override
public void onConfigurationChange(Configuration newConfig) {
- updateMediaExpansion();
mView.updateResources();
mQsSecurityFooter.onConfigurationChanged();
if (mView.isListening()) {
@@ -105,8 +101,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
- FalsingManager falsingManager, FeatureFlags featureFlags,
- MediaFlags mediaFlags) {
+ FalsingManager falsingManager, FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -117,7 +112,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mFalsingManager = falsingManager;
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
- mMediaFlags = mediaFlags;
mView.setBrightnessView(mBrightnessSliderController.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
@@ -129,7 +123,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
public void onInit() {
super.onInit();
- updateMediaExpansion();
+ mMediaHost.setExpansion(MediaHostState.EXPANDED);
mMediaHost.setShowsOnlyActiveMedia(false);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
mQsCustomizerController.init();
@@ -137,17 +131,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mQSFgsManagerFooter.init();
}
- private void updateMediaExpansion() {
- boolean inSplitShade = Utils.shouldUseSplitNotificationShade(getResources());
- float expansion;
- if (inSplitShade && !mMediaFlags.useMediaSessionLayout()) {
- expansion = MediaHostState.COLLAPSED;
- } else {
- expansion = MediaHostState.EXPANDED;
- }
- mMediaHost.setExpansion(expansion);
- }
-
@Override
protected void onViewAttached() {
super.onViewAttached();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index b2e008b9d2bc..c6ebd732518c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,7 +26,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -53,7 +52,6 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
}
};
- private final MediaFlags mMediaFlags;
private final boolean mUsingCollapsedLandscapeMedia;
@Inject
@@ -62,14 +60,12 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia,
- MediaFlags mediaFlags,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
DumpManager dumpManager
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager);
mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia;
- mMediaFlags = mediaFlags;
}
@Override
@@ -84,8 +80,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
int rotation = getRotation();
boolean isLandscape = rotation == RotationUtils.ROTATION_LANDSCAPE
|| rotation == RotationUtils.ROTATION_SEASCAPE;
- if (mMediaFlags.useMediaSessionLayout()
- && (!mUsingCollapsedLandscapeMedia || !isLandscape)) {
+ if (!mUsingCollapsedLandscapeMedia || !isLandscape) {
mMediaHost.setExpansion(MediaHost.EXPANDED);
} else {
mMediaHost.setExpansion(MediaHost.COLLAPSED);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 7fb9ef34cfd1..be6982ab2470 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -16,13 +16,14 @@ package com.android.systemui.qs.tileimpl;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Animatable2.AnimationCallback;
import android.graphics.drawable.Drawable;
@@ -53,6 +54,8 @@ public class QSIconViewImpl extends QSIconView {
@Nullable
private QSTile.Icon mLastIcon;
+ private ValueAnimator mColorAnimator = new ValueAnimator();
+
public QSIconViewImpl(Context context) {
super(context);
@@ -61,6 +64,7 @@ public class QSIconViewImpl extends QSIconView {
mIcon = createIcon();
addView(mIcon);
+ mColorAnimator.setDuration(QS_ANIM_LENGTH);
}
@Override
@@ -165,7 +169,6 @@ public class QSIconViewImpl extends QSIconView {
mState = state.state;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
- mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
((AlphaControlledSlashImageView)iv)
@@ -173,7 +176,6 @@ public class QSIconViewImpl extends QSIconView {
} else {
setTint(iv, color);
}
- mTint = color;
updateIcon(iv, state, allowAnimations);
}
} else {
@@ -191,39 +193,30 @@ public class QSIconViewImpl extends QSIconView {
((AlphaControlledSlashImageView)iv)
.setFinalImageTintList(ColorStateList.valueOf(toColor));
}
+ mColorAnimator.cancel();
if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
- final float fromAlpha = Color.alpha(fromColor);
- final float toAlpha = Color.alpha(toColor);
- final float fromChannel = Color.red(fromColor);
- final float toChannel = Color.red(toColor);
-
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- anim.setDuration(QS_ANIM_LENGTH);
- anim.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
- int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
-
- setTint(iv, Color.argb(alpha, channel, channel, channel));
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- }
+ PropertyValuesHolder values = PropertyValuesHolder.ofInt("color", fromColor, toColor);
+ values.setEvaluator(ArgbEvaluator.getInstance());
+ mColorAnimator.setValues(values);
+ mColorAnimator.removeAllListeners();
+ mColorAnimator.addUpdateListener(animation -> {
+ setTint(iv, (int) animation.getAnimatedValue());
});
- anim.start();
+ mColorAnimator.addListener(new EndRunnableAnimatorListener(endRunnable));
+
+ mColorAnimator.start();
} else {
+
setTint(iv, toColor);
endRunnable.run();
}
}
- public static void setTint(ImageView iv, int color) {
+ public void setTint(ImageView iv, int color) {
iv.setImageTintList(ColorStateList.valueOf(color));
+ mTint = color;
}
-
protected int getIconMeasureMode() {
return MeasureSpec.EXACTLY;
}
@@ -261,4 +254,25 @@ public class QSIconViewImpl extends QSIconView {
return 0;
}
}
+
+ private static class EndRunnableAnimatorListener extends AnimatorListenerAdapter {
+ private Runnable mRunnable;
+
+ EndRunnableAnimatorListener(Runnable endRunnable) {
+ super();
+ mRunnable = endRunnable;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mRunnable.run();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mRunnable.run();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 3c6ab34733e5..bbba0071094b 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -199,7 +199,7 @@ public class ScrimDrawable extends Drawable {
drawConcave(canvas);
} else if (mCornerRadiusEnabled && mCornerRadius > 0) {
canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
- getBounds().bottom + mCornerRadius,
+ getBounds().bottom,
/* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
} else {
canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index d51aaad46432..55ca8f3fd634 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -575,13 +575,7 @@ public class KeyguardIndicationController {
return;
}
int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
- try {
- mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
- mIActivityManager.stopUser(currentUserId, true /* force */,
- null);
- } catch (RemoteException re) {
- Log.e(TAG, "Failed to logout user", re);
- }
+ mDevicePolicyManager.logoutUser();
})
.build(),
false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index de3e89d6dc8c..4732a8c09500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -34,11 +34,11 @@ 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.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
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.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
import java.io.FileDescriptor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 3fe108f2c951..f0e01a33fc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -192,7 +192,10 @@ constructor(
override fun onTouchEvent(event: MotionEvent): Boolean {
val finishExpanding = (event.action == MotionEvent.ACTION_CANCEL ||
event.action == MotionEvent.ACTION_UP) && isExpanding
- if (!canHandleMotionEvent() && !finishExpanding) {
+
+ val isDraggingNotificationOrCanBypass = mStartingChild?.showingPulsing() == true ||
+ bypassController.canBypass()
+ if ((!canHandleMotionEvent() || !isDraggingNotificationOrCanBypass) && !finishExpanding) {
// We allow cancellations/finishing to still go through here to clean up the state
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 1237c70fcbe3..aac5b8d6b2eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -70,7 +70,7 @@ public class KeyguardCoordinator implements Coordinator {
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- return mKeyguardNotificationVisibilityProvider.hideNotification(entry);
+ return mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 699c4e77321e..acc493d4b992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -254,10 +254,14 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
+ pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing);
+ pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity);
pw.println("reorderingAllowed: " + mReorderingAllowed);
pw.println(" screenOn: " + mScreenOn);
pw.println(" panelExpanded: " + mPanelExpanded);
pw.println(" pulsing: " + mPulsing);
+ pw.println("isSuppressingPipelineRun: " + mIsSuppressingPipelineRun);
pw.println("isSuppressingGroupChange: " + mIsSuppressingGroupChange);
pw.println("isSuppressingEntryReorder: " + mIsSuppressingEntryReorder);
pw.println("entriesWithSuppressedSectionChange: "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index efe88e6d4f0a..34c8044ef0d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -73,6 +73,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProviderModule;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -107,9 +108,10 @@ import dagger.Provides;
*/
@Module(includes = {
CoordinatorsModule.class,
+ KeyguardNotificationVisibilityProviderModule.class,
NotifActivityLaunchEventsModule.class,
- NotifPipelineChoreographerModule.class,
NotifPanelEventsModule.class,
+ NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class,
})
public interface NotificationsModule {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index 70c9a16b9d7a..ff0e47f3cfec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -14,6 +14,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -22,13 +23,49 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SecureSettings
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import java.util.function.Consumer
import javax.inject.Inject
-/**
- * Determines if notifications should be visible based on the state of the keyguard
- */
-class KeyguardNotificationVisibilityProvider @Inject constructor(
+/** Determines if notifications should be visible based on the state of the keyguard. */
+interface KeyguardNotificationVisibilityProvider {
+ /**
+ * Determines if the given notification should be hidden based on the current keyguard state.
+ * If a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
+ * method may no longer be valid and should be re-queried.
+ */
+ fun shouldHideNotification(entry: NotificationEntry): Boolean
+
+ /** Registers a listener to be notified when the internal keyguard state has been updated. */
+ fun addOnStateChangedListener(listener: Consumer<String>)
+
+ /** Unregisters a listener previously registered with [addOnStateChangedListener]. */
+ fun removeOnStateChangedListener(listener: Consumer<String>)
+}
+
+/** Provides a [KeyguardNotificationVisibilityProvider] in [SysUISingleton] scope. */
+@Module(includes = [KeyguardNotificationVisibilityProviderImplModule::class])
+object KeyguardNotificationVisibilityProviderModule
+
+@Module
+private interface KeyguardNotificationVisibilityProviderImplModule {
+ @Binds
+ fun bindImpl(impl: KeyguardNotificationVisibilityProviderImpl):
+ KeyguardNotificationVisibilityProvider
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardNotificationVisibilityProvider::class)
+ fun bindStartable(impl: KeyguardNotificationVisibilityProviderImpl): CoreStartable
+}
+
+@SysUISingleton
+private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
context: Context,
@Main private val handler: Handler,
private val keyguardStateController: KeyguardStateController,
@@ -36,8 +73,10 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val highPriorityProvider: HighPriorityProvider,
private val statusBarStateController: StatusBarStateController,
- private val broadcastDispatcher: BroadcastDispatcher
-) : CoreStartable(context) {
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val secureSettings: SecureSettings,
+ private val globalSettings: GlobalSettings
+) : CoreStartable(context), KeyguardNotificationVisibilityProvider {
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
@@ -60,33 +99,28 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
// register lockscreen settings changed callbacks:
val settingsObserver: ContentObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
if (keyguardStateController.isShowing) {
notifyStateChanged("Settings $uri changed")
}
}
}
- mContext.contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS),
- false,
+ secureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
settingsObserver,
UserHandle.USER_ALL)
- mContext.contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ secureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
true,
settingsObserver,
UserHandle.USER_ALL)
- mContext.contentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
- false,
- settingsObserver)
+ globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver)
- mContext.contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
- false,
+ secureSettings.registerContentObserverForUser(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
settingsObserver,
UserHandle.USER_ALL)
@@ -98,41 +132,36 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
})
broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- if (keyguardStateController.isShowing()) {
+ if (keyguardStateController.isShowing) {
// maybe public mode changed
- notifyStateChanged(intent.action)
+ notifyStateChanged(intent.action!!)
}
}
}, IntentFilter(Intent.ACTION_USER_SWITCHED))
}
- fun addOnStateChangedListener(listener: Consumer<String>) {
+ override fun addOnStateChangedListener(listener: Consumer<String>) {
onStateChangedListeners.addIfAbsent(listener)
}
- fun removeOnStateChangedListener(listener: Consumer<String>) {
+ override fun removeOnStateChangedListener(listener: Consumer<String>) {
onStateChangedListeners.remove(listener)
}
private fun notifyStateChanged(reason: String) {
- onStateChangedListeners.forEach({ it.accept(reason) })
+ onStateChangedListeners.forEach { it.accept(reason) }
}
- /**
- * Determines if the given notification should be hidden based on the current keyguard state.
- * If Listener#onKeyguardStateChanged is invoked, the results of this method may no longer
- * be valid, and so should be re-queried
- */
- fun hideNotification(entry: NotificationEntry): Boolean {
+ override fun shouldHideNotification(entry: NotificationEntry): Boolean {
val sbn = entry.sbn
// FILTER OUT the notification when the keyguard is showing and...
- if (keyguardStateController.isShowing()) {
+ if (keyguardStateController.isShowing) {
// ... user settings or the device policy manager doesn't allow lockscreen
// notifications;
if (!lockscreenUserManager.shouldShowLockscreenNotifications()) {
return true
}
- val currUserId: Int = lockscreenUserManager.getCurrentUserId()
+ val currUserId: Int = lockscreenUserManager.currentUserId
val notifUserId =
if (sbn.user.identifier == UserHandle.USER_ALL) currUserId
else sbn.user.identifier
@@ -178,9 +207,7 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
}
private fun readShowSilentNotificationSetting() {
- hideSilentNotificationsOnLockscreen = Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
- 1) == 0
+ hideSilentNotificationsOnLockscreen =
+ secureSettings.getBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index c1771ccce9d0..e210f193b0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -312,7 +312,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
- if (mKeyguardNotificationVisibilityProvider.hideNotification(entry)) {
+ if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
mLogger.keyguardHideNotification(entry.getKey());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 4b0e2ffd5d7f..41eeada0fcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -21,7 +21,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
-import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -82,16 +81,8 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso
@Override
protected Drawable doInBackground(Uri... uris) {
- Drawable drawable = null;
Uri target = uris[0];
-
- try {
- drawable = mResolver.resolveImage(target);
- } catch (IOException | SecurityException ex) {
- Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex);
- }
-
- return drawable;
+ return mResolver.resolveImage(target);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 44ccb68cce4a..b05e64ab1991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -31,7 +31,6 @@ import com.android.internal.widget.ImageResolver;
import com.android.internal.widget.LocalImageResolver;
import com.android.internal.widget.MessagingMessage;
-import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -111,30 +110,30 @@ public class NotificationInlineImageResolver implements ImageResolver {
* To resolve image from specified uri directly. If the resulting image is larger than the
* maximum allowed size, scale it down.
* @param uri Uri of the image.
- * @return Drawable of the image.
- * @throws IOException Throws if failed at resolving the image.
+ * @return Drawable of the image, or null if unable to load.
*/
- Drawable resolveImage(Uri uri) throws IOException {
- return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight);
+ Drawable resolveImage(Uri uri) {
+ try {
+ return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight);
+ } catch (Exception ex) {
+ // Catch general Exception because ContentResolver can re-throw arbitrary Exception
+ // from remote process as a RuntimeException. See: Parcel#readException
+ Log.d(TAG, "resolveImage: Can't load image from " + uri, ex);
+ }
+ return null;
}
@Override
public Drawable loadImage(Uri uri) {
- Drawable result = null;
- try {
- if (hasCache()) {
- // if the uri isn't currently cached, try caching it first
- if (!mImageCache.hasEntry(uri)) {
- mImageCache.preload((uri));
- }
- result = mImageCache.get(uri);
- } else {
- result = resolveImage(uri);
- }
- } catch (IOException | SecurityException ex) {
- Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
+ return hasCache() ? loadImageFromCache(uri) : resolveImage(uri);
+ }
+
+ private Drawable loadImageFromCache(Uri uri) {
+ // if the uri isn't currently cached, try caching it first
+ if (!mImageCache.hasEntry(uri)) {
+ mImageCache.preload((uri));
}
- return result;
+ return mImageCache.get(uri);
}
/**
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 c89f4d797819..5c68559ffb1e 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
@@ -689,7 +689,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean showFooterView = (showDismissView || mController.getVisibleNotificationCount() > 0)
&& mIsCurrentUserSetup // see: b/193149550
&& mStatusBarState != StatusBarState.KEYGUARD
- && mQsExpansionFraction != 1
+ // quick settings don't affect notifications when not in full screen
+ && (mQsExpansionFraction != 1 || !mQsFullScreen)
&& !mScreenOffAnimationController.shouldHideNotificationsFooter()
&& !mIsRemoteInputActive;
boolean showHistory = mController.isHistoryEnabled();
@@ -799,6 +800,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace);
drawDebugInfo(canvas, y, Color.RED, /* label= */
"mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y);
+
+ drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY,
+ /* label= */ "mRoundedRectClippingBottom) = " + y);
}
private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
@@ -1311,7 +1315,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mOnStackYChanged != null) {
mOnStackYChanged.accept(listenerNeedsAnimation);
}
- if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
+ if ((mQsExpansionFraction <= 0 || !mQsFullScreen) && !shouldSkipHeightUpdate()) {
final float endHeight = updateStackEndHeight();
updateStackHeight(endHeight, fraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index ec2d608b2683..c2c8bd3fad5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -1695,12 +1695,20 @@ public class CentralSurfaces extends CoreStartable implements
public void startActivity(Intent intent, boolean dismissShade,
@Nullable ActivityLaunchAnimator.Controller animationController,
boolean showOverLockscreenWhenLocked) {
+ startActivity(intent, dismissShade, animationController, showOverLockscreenWhenLocked,
+ UserHandle.CURRENT);
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked, UserHandle userHandle) {
// Make sure that we dismiss the keyguard if it is directly dismissable or when we don't
// want to show the activity above it.
if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) {
startActivityDismissingKeyguard(intent, false, dismissShade,
- false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
- 0 /* flags */, animationController);
+ false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
+ 0 /* flags */, animationController, userHandle);
return;
}
@@ -1755,7 +1763,7 @@ public class CentralSurfaces extends CoreStartable implements
.create(mContext)
.addNextIntent(intent)
.startActivities(getActivityOptions(getDisplayId(), adapter),
- UserHandle.CURRENT));
+ userHandle));
}
/**
@@ -1775,7 +1783,7 @@ public class CentralSurfaces extends CoreStartable implements
public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
startActivityDismissingKeyguard(intent, false, dismissShade,
false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
- null /* animationController */);
+ null /* animationController */, UserHandle.CURRENT);
}
public void setQsExpanded(boolean expanded) {
@@ -1869,13 +1877,7 @@ public class CentralSurfaces extends CoreStartable implements
if (!mPresenter.isCollapsing()) {
onClosingFinished();
}
-
- // Collapse the panel if we're launching in fullscreen, over the lockscreen. Do not do this
- // if the device has gone back to sleep - through a horrific chain of 15 or so function
- // calls, instantCollapseNotificationPanel will eventually call through to
- // StatusBar#wakeUpIfDozing, which will wake the device up even if it was put to sleep
- // during the launch animation.
- if (launchIsFullScreen && mPowerManager.isInteractive()) {
+ if (launchIsFullScreen) {
instantCollapseNotificationPanel();
}
}
@@ -2417,7 +2419,7 @@ public class CentralSurfaces extends CoreStartable implements
boolean dismissShade, int flags) {
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
- flags, null /* animationController */);
+ flags, null /* animationController */, UserHandle.CURRENT);
}
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
@@ -2428,7 +2430,8 @@ public class CentralSurfaces extends CoreStartable implements
void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
final Callback callback, int flags,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ final UserHandle userHandle) {
if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
final boolean willLaunchResolverActivity =
@@ -2487,7 +2490,7 @@ public class CentralSurfaces extends CoreStartable implements
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
- options.toBundle(), UserHandle.CURRENT.getIdentifier());
+ options.toBundle(), userHandle.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start activity", e);
}
@@ -2860,7 +2863,8 @@ public class CentralSurfaces extends CoreStartable implements
false /* disallowEnterPictureInPictureWhileLaunching */,
null /* callback */,
0 /* flags */,
- animationController),
+ animationController,
+ UserHandle.CURRENT),
delay);
}
@@ -3126,6 +3130,10 @@ public class CentralSurfaces extends CoreStartable implements
public void finishKeyguardFadingAway() {
mKeyguardStateController.notifyKeyguardDoneFading();
mScrimController.setExpansionAffectsAlpha(true);
+
+ // If the device was re-locked while unlocking, we might have a pending lock that was
+ // delayed because the keyguard was in the middle of going away.
+ mKeyguardViewMediator.maybeHandlePendingLock();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 536be1c1a866..c4e655a45ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -371,7 +371,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */);
+ null /* animationController */, UserHandle.CURRENT);
} else {
if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -428,7 +428,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */);
+ null /* animationController */, UserHandle.CURRENT);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 324d47eed1db..799e5feb1586 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.content.Intent;
+import android.os.UserHandle;
import android.os.UserManager;
import android.view.View;
import android.view.ViewGroup;
@@ -62,7 +63,7 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
mActivityStarter.startActivity(intent, true /* dismissShade */,
ActivityLaunchAnimator.Controller.fromView(v, null),
- true /* showOverlockscreenwhenlocked */);
+ true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM);
} else {
mUserSwitchDialogController.showDialog(v);
}
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 5746ffb6debe..6cca9045d867 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1098,6 +1098,8 @@ public class NotificationPanelViewController extends PanelViewController {
if (splitNotificationShadeChanged) {
updateClockAppearance();
+ updateQsState();
+ mNotificationStackScrollLayoutController.updateFooter();
}
}
@@ -2348,7 +2350,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int calculateBottomQsClippingBound(int top) {
if (mShouldUseSplitNotificationShade) {
return top + mNotificationStackScrollLayoutController.getHeight()
- - mSplitShadeNotificationsScrimMarginBottom;
+ + mSplitShadeNotificationsScrimMarginBottom;
} else {
return getView().getBottom();
}
@@ -2466,7 +2468,12 @@ public class NotificationPanelViewController extends PanelViewController {
// be visible, otherwise you can see the bounds once swiping up to see bouncer
mScrimController.setNotificationsBounds(0, 0, 0, 0);
} else {
- mScrimController.setNotificationsBounds(left, top, right, bottom);
+ // Increase the height of the notifications scrim when not in split shade
+ // (e.g. portrait tablet) so the rounded corners are not visible at the bottom,
+ // in this case they are rendered off-screen
+ final int notificationsScrimBottom =
+ mShouldUseSplitNotificationShade ? bottom : bottom + radius;
+ mScrimController.setNotificationsBounds(left, top, right, notificationsScrimBottom);
}
if (mShouldUseSplitNotificationShade) {
@@ -2476,10 +2483,13 @@ public class NotificationPanelViewController extends PanelViewController {
}
mScrimController.setScrimCornerRadius(radius);
+
+ // Convert global clipping coordinates to local ones,
+ // relative to NotificationStackScrollLayout
int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
- int nsslBottom = bottom;
+ int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
mNotificationStackScrollLayoutController.setRoundedClippingBounds(
nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index 7764d33856ca..a15feeb9cda8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -4,6 +4,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets
+import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -11,6 +12,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.navigationbar.NavigationModeController
@@ -21,14 +23,19 @@ import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.util.Utils
import com.android.systemui.util.ViewController
+import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.function.Consumer
import javax.inject.Inject
+@VisibleForTesting
+internal const val INSET_DEBOUNCE_MILLIS = 500L
+
class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
- private val featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags,
+ @Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
var qsExpanded = false
@@ -60,11 +67,29 @@ class NotificationsQSContainerController @Inject constructor(
taskbarVisible = visible
}
}
- private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
- // when taskbar is visible, stableInsetBottom will include its height
- bottomStableInsets = insets.stableInsetBottom
- bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
- updateBottomSpacing()
+
+ // With certain configuration changes (like light/dark changes), the nav bar will disappear
+ // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value
+ // for 500ms.
+ // All interactions with this object happen in the main thread.
+ private val delayedInsetSetter = object : Runnable, Consumer<WindowInsets> {
+ private var canceller: Runnable? = null
+ private var stableInsets = 0
+ private var cutoutInsets = 0
+
+ override fun accept(insets: WindowInsets) {
+ // when taskbar is visible, stableInsetBottom will include its height
+ stableInsets = insets.stableInsetBottom
+ cutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
+ canceller?.run()
+ canceller = delayableExecutor.executeDelayed(this, INSET_DEBOUNCE_MILLIS)
+ }
+
+ override fun run() {
+ bottomStableInsets = stableInsets
+ bottomCutoutInsets = cutoutInsets
+ updateBottomSpacing()
+ }
}
override fun onInit() {
@@ -77,7 +102,7 @@ class NotificationsQSContainerController @Inject constructor(
public override fun onViewAttached() {
updateResources()
overviewProxyService.addCallback(taskbarVisibilityListener)
- mView.setInsetsChangedListener(windowInsetsListener)
+ mView.setInsetsChangedListener(delayedInsetSetter)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
mView.setConfigurationChangedListener { updateResources() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c160c22203a5..23a1087edfd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1016,6 +1016,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
mCentralSurfaces.setBouncerShowing(bouncerShowing);
+ mKeyguardMessageAreaController.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 79d646cdbd13..d26b378650e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -63,6 +63,8 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
+ private static final int DEFAULT_THEME = R.style.Theme_SystemUI_Dialog;
+ private static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
private final Context mContext;
@Nullable private final DismissReceiver mDismissReceiver;
@@ -78,11 +80,15 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
private List<Runnable> mOnCreateRunnables = new ArrayList<>();
public SystemUIDialog(Context context) {
- this(context, R.style.Theme_SystemUI_Dialog);
+ this(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK);
}
public SystemUIDialog(Context context, int theme) {
- this(context, theme, true /* dismissOnDeviceLock */);
+ this(context, theme, DEFAULT_DISMISS_ON_DEVICE_LOCK);
+ }
+
+ public SystemUIDialog(Context context, boolean dismissOnDeviceLock) {
+ this(context, DEFAULT_THEME, dismissOnDeviceLock);
}
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
index 909261f0eb7e..62549a70897b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.userswitcher
import android.content.Intent
+import android.os.UserHandle
import android.view.View
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.flags.FeatureFlags
@@ -67,7 +68,7 @@ class StatusBarUserSwitcherControllerImpl @Inject constructor(
activityStarter.startActivity(intent, true /* dismissShade */,
ActivityLaunchAnimator.Controller.fromView(view, null),
- true /* showOverlockscreenwhenlocked */)
+ true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM)
} else {
userSwitcherDialogController.showDialog(view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 4d6d05f2b628..006edcaf41de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -903,11 +903,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
this.radius = radius;
}
- Animator createCircularRevealAnimator(View view) {
+ Animator createCircularHideAnimator(View view) {
return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, radius, 0);
}
- Animator createCircularHideAnimator(View view) {
+ Animator createCircularRevealAnimator(View view) {
return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0, radius);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index eb8c247ca956..ca5edb5c3fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -447,14 +447,6 @@ public class UserSwitcherController implements Dumpable {
mResumeUserOnGuestLogout = resume;
}
- public void logoutCurrentUser() {
- int currentUser = mUserTracker.getUserId();
- if (currentUser != UserHandle.USER_SYSTEM) {
- pauseRefreshUsers();
- ActivityManager.logoutCurrentUser();
- }
- }
-
/**
* Returns whether the current user is a system user.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
index 5aaf7f680d5c..1bf5f076ac2f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -303,6 +303,7 @@ public interface SettingsProxy {
default boolean putInt(String name, int value) {
return putIntForUser(name, value, getUserId());
}
+
/** See {@link #putInt(String, int)}. */
default boolean putIntForUser(String name, int value, int userHandle) {
return putStringForUser(name, Integer.toString(value), userHandle);
@@ -310,6 +311,76 @@ public interface SettingsProxy {
/**
* Convenience function for retrieving a single secure settings value
+ * as a boolean. Note that internally setting values are always
+ * stored as strings; this function converts the string to a boolean
+ * for you. The default value will be returned if the setting is
+ * not defined or not a boolean.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def Value to return if the setting is not defined.
+ *
+ * @return The setting's current value, or 'def' if it is not defined
+ * or not a valid boolean.
+ */
+ default boolean getBool(String name, boolean def) {
+ return getBoolForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getBool(String, boolean)}. */
+ default boolean getBoolForUser(String name, boolean def, int userHandle) {
+ return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
+ * as a boolean. Note that internally setting values are always
+ * stored as strings; this function converts the string to a boolean
+ * for you.
+ * <p>
+ * This version does not take a default value. If the setting has not
+ * been set, or the string value is not a number,
+ * it throws {@link Settings.SettingNotFoundException}.
+ *
+ * @param name The name of the setting to retrieve.
+ *
+ * @throws Settings.SettingNotFoundException Thrown if a setting by the given
+ * name can't be found or the setting value is not a boolean.
+ *
+ * @return The setting's current value.
+ */
+ default boolean getBool(String name) throws Settings.SettingNotFoundException {
+ return getBoolForUser(name, getUserId());
+ }
+
+ /** See {@link #getBool(String)}. */
+ default boolean getBoolForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ return getIntForUser(name, userHandle) != 0;
+ }
+
+ /**
+ * Convenience function for updating a single settings value as a
+ * boolean. This will either create a new entry in the table if the
+ * given name does not exist, or modify the value of the existing row
+ * with that name. Note that internally setting values are always
+ * stored as strings, so this function converts the given value to a
+ * string before storing it.
+ *
+ * @param name The name of the setting to modify.
+ * @param value The new value for the setting.
+ * @return true if the value was set, false on database errors
+ */
+ default boolean putBool(String name, boolean value) {
+ return putBoolForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putBool(String, boolean)}. */
+ default boolean putBoolForUser(String name, boolean value, int userHandle) {
+ return putIntForUser(name, value ? 1 : 0, userHandle);
+ }
+
+ /**
+ * Convenience function for retrieving a single secure settings value
* as a {@code long}. Note that internally setting values are always
* stored as strings; this function converts the string to a {@code long}
* for you. The default value will be returned if the setting is
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index a7197cca530c..1518ea160016 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -84,4 +84,10 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
mMessageAreaController.setMessage("");
verify(mKeyguardMessageArea).setMessage("");
}
+
+ @Test
+ public void testSetBouncerVisible() {
+ mMessageAreaController.setBouncerShowing(true);
+ verify(mKeyguardMessageArea).setBouncerShowing(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index 31fb25a7a89c..013c298d69d7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -40,7 +40,7 @@ public class KeyguardMessageAreaTest extends SysuiTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
- mKeyguardMessageArea.setBouncerVisible(true);
+ mKeyguardMessageArea.setBouncerShowing(true);
}
@Test
@@ -53,7 +53,7 @@ public class KeyguardMessageAreaTest extends SysuiTestCase {
@Test
public void testHiddenWhenBouncerHidden() {
- mKeyguardMessageArea.setBouncerVisible(false);
+ mKeyguardMessageArea.setBouncerShowing(false);
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
mKeyguardMessageArea.setMessage("oobleck");
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index ec92adb0f48c..ac78626ff126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -33,7 +33,6 @@ import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -61,9 +60,11 @@ import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -102,7 +103,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThreadFactory mThreadFactory;
- private ArrayList<DecorProvider> mDecorProviders;
+ private ArrayList<DecorProvider> mPrivacyDecorProviders;
@Mock
private Display mDisplay;
@Mock
@@ -197,17 +198,43 @@ public class ScreenDecorationsTest extends SysuiTestCase {
reset(mTunerService);
}
+ @NonNull
+ private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) {
+ switch (overlayId) {
+ case BOUNDS_POSITION_LEFT:
+ return new int[] {
+ R.id.rounded_corner_top_left,
+ R.id.rounded_corner_top_left };
+ case BOUNDS_POSITION_TOP:
+ return new int[] {
+ R.id.rounded_corner_top_left,
+ R.id.rounded_corner_top_right };
+ case BOUNDS_POSITION_RIGHT:
+ return new int[] {
+ R.id.rounded_corner_top_right,
+ R.id.rounded_corner_bottom_right };
+ case BOUNDS_POSITION_BOTTOM:
+ return new int[] {
+ R.id.rounded_corner_bottom_left,
+ R.id.rounded_corner_bottom_right };
+ default:
+ throw new IllegalArgumentException("unknown overlayId: " + overlayId);
+ }
+ }
- private void verifyRoundedCornerViewsVisibility(
+ private void verifyRoundedCornerViewsExist(
@DisplayCutout.BoundsPosition final int overlayId,
- @View.Visibility final int visibility) {
+ @View.Visibility final boolean isExist) {
final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView();
- final View left = overlay.findViewById(R.id.left);
- final View right = overlay.findViewById(R.id.right);
- assertNotNull(left);
- assertNotNull(right);
- assertThat(left.getVisibility()).isEqualTo(visibility);
- assertThat(right.getVisibility()).isEqualTo(visibility);
+ for (int id: getRoundCornerIdsFromOverlayId(overlayId)) {
+ final View view = overlay.findViewById(id);
+ if (isExist) {
+ assertNotNull(view);
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ } else {
+ assertNull(view);
+ }
+ }
}
@Nullable
@@ -351,8 +378,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall not exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
@@ -380,8 +407,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall not exist
verifyDotViewsNullable(true);
@@ -408,8 +435,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
@@ -449,21 +476,26 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mScreenDecorations.start();
View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
- .findViewById(R.id.left);
+ .findViewById(R.id.rounded_corner_top_left);
View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
- .findViewById(R.id.right);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius));
- verify(mScreenDecorations, atLeastOnce())
- .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius));
+ .findViewById(R.id.rounded_corner_top_right);
+ ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams();
+ ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams();
+ assertEquals(leftParams.width, testTopRadius);
+ assertEquals(leftParams.height, testTopRadius);
+ assertEquals(rightParams.width, testTopRadius);
+ assertEquals(rightParams.height, testTopRadius);
+
leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
- .findViewById(R.id.left);
+ .findViewById(R.id.rounded_corner_bottom_left);
rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
- .findViewById(R.id.right);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius));
- verify(mScreenDecorations, atLeastOnce())
- .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius));
+ .findViewById(R.id.rounded_corner_bottom_right);
+ leftParams = leftRoundedCorner.getLayoutParams();
+ rightParams = rightRoundedCorner.getLayoutParams();
+ assertEquals(leftParams.width, testBottomRadius);
+ assertEquals(leftParams.height, testBottomRadius);
+ assertEquals(rightParams.width, testBottomRadius);
+ assertEquals(rightParams.height, testBottomRadius);
}
@Test
@@ -479,31 +511,27 @@ public class ScreenDecorationsTest extends SysuiTestCase {
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
- final Size topRadius = new Size(testTopRadius, testTopRadius);
- final Size bottomRadius = new Size(testBottomRadius, testBottomRadius);
- View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
- .findViewById(R.id.left);
- boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
-
- View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
- .findViewById(R.id.right);
- isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
-
- leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
- .findViewById(R.id.left);
- isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
-
- rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
- .findViewById(R.id.right);
- isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right);
- verify(mScreenDecorations, atLeastOnce())
- .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
+ View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+ .findViewById(R.id.rounded_corner_top_left);
+ View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+ .findViewById(R.id.rounded_corner_bottom_left);
+ ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams();
+ ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams();
+ assertEquals(topParams.width, testTopRadius);
+ assertEquals(topParams.height, testTopRadius);
+ assertEquals(bottomParams.width, testBottomRadius);
+ assertEquals(bottomParams.height, testBottomRadius);
+
+ topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+ .findViewById(R.id.rounded_corner_top_right);
+ bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+ .findViewById(R.id.rounded_corner_bottom_right);
+ topParams = topRoundedCorner.getLayoutParams();
+ bottomParams = bottomRoundedCorner.getLayoutParams();
+ assertEquals(topParams.width, testTopRadius);
+ assertEquals(topParams.height, testTopRadius);
+ assertEquals(bottomParams.width, testBottomRadius);
+ assertEquals(bottomParams.height, testBottomRadius);
}
@Test
@@ -523,8 +551,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall not exist
verifyDotViewsNullable(true);
@@ -557,8 +585,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
@@ -617,10 +645,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
// Top rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
// Bottom rounded corner views shall exist because of privacy dot
// but be gone because of no rounded corner
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
// Privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
@@ -648,7 +676,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
// Left rounded corner views shall exist because of cutout
// but be gone because of no rounded corner
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false);
// Top privacy dots shall not exist because of no privacy
verifyDotViewsNullable(true);
@@ -700,8 +728,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Top privacy dots shall not exist because of no privacy dot
verifyDotViewsNullable(true);
@@ -728,8 +756,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(false, true, false, true);
// Rounded corner views shall exist
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
// Top privacy dots shall exist but invisible
verifyDotViewsVisibility(View.INVISIBLE);
@@ -859,7 +887,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
verifyOverlaysExistAndAdded(true, false, true, false);
// Verify each privacy dot id appears only once
- mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
+ mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
int findCount = 0;
for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
if (overlay == null) {
@@ -913,8 +941,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
// Both top and bottom windows should be added because of privacy dot,
// but their visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
when(mContext.getResources().getBoolean(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
@@ -925,8 +953,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
// Both top and bottom windows should be added because of privacy dot,
// but their visibility shall be gone because of no rounding.
verifyOverlaysExistAndAdded(false, true, false, true);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
- verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+ verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
}
@Test
@@ -1174,14 +1202,14 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
- mDecorProviders = new ArrayList<>();
+ mPrivacyDecorProviders = new ArrayList<>();
if (privacyDot) {
- mDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
- mDecorProviders.add(mPrivacyDotTopRightDecorProvider);
- mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
- mDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
+ mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
+ mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider);
+ mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
+ mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
}
- when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders);
+ when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders);
when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index 214fd4d28398..8eb0918beedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewBoundAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -19,7 +19,7 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class ViewBoundAnimatorTest : SysuiTestCase() {
+class ViewHierarchyAnimatorTest : SysuiTestCase() {
companion object {
private const val TEST_DURATION = 1000L
private val TEST_INTERPOLATOR = Interpolators.LINEAR
@@ -34,14 +34,14 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
@After
fun tearDown() {
- ViewBoundAnimator.stopAnimating(rootView)
+ ViewHierarchyAnimator.stopAnimating(rootView)
}
@Test
fun respectsAnimationParameters() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(
+ ViewHierarchyAnimator.animate(
rootView, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION
)
rootView.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
@@ -56,7 +56,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesFromStartToEnd() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -73,7 +73,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesSuccessiveLayoutChanges() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -103,7 +103,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun animatesFromPreviousAnimationProgress() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
+ ViewHierarchyAnimator.animateNextUpdate(rootView, interpolator = TEST_INTERPOLATOR)
// Change all bounds.
rootView.layout(0 /* l */, 20 /* t */, 70 /* r */, 80 /* b */)
@@ -131,7 +131,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
firstChild.layout(0 /* l */, 0 /* t */, 100 /* r */, 100 /* b */)
secondChild.layout(100 /* l */, 0 /* t */, 150 /* r */, 100 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */)
firstChild.layout(10 /* l */, 20 /* t */, 150 /* r */, 120 /* b */)
@@ -154,7 +154,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateInvisibleViews() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// GONE.
rootView.visibility = View.GONE
rootView.layout(0 /* l */, 15 /* t */, 55 /* r */, 80 /* b */)
@@ -171,7 +171,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateUnchangingBounds() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// No bounds are changed.
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
@@ -191,9 +191,9 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun doesNotAnimateExcludedBounds() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(
+ ViewHierarchyAnimator.animate(
rootView,
- bounds = setOf(ViewBoundAnimator.Bound.LEFT, ViewBoundAnimator.Bound.TOP),
+ bounds = setOf(ViewHierarchyAnimator.Bound.LEFT, ViewHierarchyAnimator.Bound.TOP),
interpolator = TEST_INTERPOLATOR
)
// Change all bounds.
@@ -211,7 +211,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun stopsAnimatingAfterSingleLayout() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animateNextUpdate(rootView)
+ ViewHierarchyAnimator.animateNextUpdate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -231,7 +231,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
fun stopsAnimatingWhenInstructed() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
- ViewBoundAnimator.animate(rootView)
+ ViewHierarchyAnimator.animate(rootView)
// Change all bounds.
rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
@@ -240,7 +240,7 @@ class ViewBoundAnimatorTest : SysuiTestCase() {
assertNull(rootView.getTag(R.id.tag_animator))
checkBounds(rootView, l = 0, t = 15, r = 70, b = 80)
- ViewBoundAnimator.stopAnimating(rootView)
+ ViewHierarchyAnimator.stopAnimating(rootView)
// Change all bounds again.
rootView.layout(10 /* l */, 10 /* t */, 50/* r */, 50 /* b */)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
index ca74df0a23c5..5182210b9567 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -19,25 +19,19 @@ package com.android.systemui.decor
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.DisplayCutout
-import android.view.LayoutInflater
import android.view.Surface
import android.view.View
-import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.eq
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@@ -45,62 +39,88 @@ import org.mockito.Mockito.`when` as whenever
class OverlayWindowTest : SysuiTestCase() {
companion object {
- private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container
- private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right
+ private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container
+ private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_right_container
}
private lateinit var overlay: OverlayWindow
-
- @Mock private lateinit var layoutInflater: LayoutInflater
- @Mock private lateinit var decorProvider: DecorProvider
+ private lateinit var decorProvider1: DecorProvider
+ private lateinit var decorProvider2: DecorProvider
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- layoutInflater = spy(LayoutInflater.from(mContext))
-
- overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
-
- whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID)
- whenever(decorProvider.inflateView(
- eq(layoutInflater),
- eq(overlay.rootView),
- anyInt())
- ).then {
- val layoutInflater = it.getArgument<LayoutInflater>(0)
- val parent = it.getArgument<ViewGroup>(1)
- layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent)
- return@then parent.getChildAt(parent.childCount - 1)
- }
- }
+ decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl(
+ viewId = TEST_DECOR_VIEW_ID_1,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+ layoutId = R.layout.privacy_dot_top_left))
+ decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl(
+ viewId = TEST_DECOR_VIEW_ID_2,
+ alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+ alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+ layoutId = R.layout.privacy_dot_bottom_right))
- @Test
- fun testAnyBoundsPositionShallNoExceptionForConstructor() {
- OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT)
- OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP)
- OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
- OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM)
+ overlay = OverlayWindow(mContext)
}
@Test
fun testAddProvider() {
@Surface.Rotation val rotation = Surface.ROTATION_270
- overlay.addDecorProvider(decorProvider, rotation)
- verify(decorProvider, Mockito.times(1)).inflateView(
- eq(layoutInflater), eq(overlay.rootView), eq(rotation))
- val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
- Assert.assertNotNull(viewFoundFromRootView)
- Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID))
+ overlay.addDecorProvider(decorProvider1, rotation)
+ overlay.addDecorProvider(decorProvider2, rotation)
+
+ verify(decorProvider1, times(1)).inflateView(
+ mContext, overlay.rootView, rotation)
+ verify(decorProvider2, times(1)).inflateView(
+ mContext, overlay.rootView, rotation)
+
+ val view1FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1)
+ Assert.assertNotNull(view1FoundFromRootView)
+ Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1))
+ val view2FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_2)
+ Assert.assertNotNull(view2FoundFromRootView)
+ Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2))
}
@Test
fun testRemoveView() {
- @Surface.Rotation val rotation = Surface.ROTATION_270
- overlay.addDecorProvider(decorProvider, rotation)
- overlay.removeView(TEST_DECOR_VIEW_ID)
- val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
+ overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270)
+ overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270)
+ overlay.removeView(TEST_DECOR_VIEW_ID_1)
+
+ val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1)
Assert.assertNull(viewFoundFromRootView)
- Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID))
+ Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1))
+ }
+
+ @Test
+ fun testOnReloadResAndMeasureWithoutIds() {
+ overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0)
+ overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0)
+
+ overlay.onReloadResAndMeasure(
+ reloadToken = 1,
+ rotation = Surface.ROTATION_90,
+ displayUniqueId = null)
+ verify(decorProvider1, times(1)).onReloadResAndMeasure(
+ overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null)
+ verify(decorProvider2, times(1)).onReloadResAndMeasure(
+ overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null)
+ }
+
+ @Test
+ fun testOnReloadResAndMeasureWithIds() {
+ overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0)
+ overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0)
+
+ overlay.onReloadResAndMeasure(
+ filterIds = arrayOf(TEST_DECOR_VIEW_ID_2),
+ reloadToken = 1,
+ rotation = Surface.ROTATION_90,
+ displayUniqueId = null)
+ verify(decorProvider1, never()).onReloadResAndMeasure(
+ overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null)
+ verify(decorProvider2, times(1)).onReloadResAndMeasure(
+ overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
index bac08176d2eb..171b76748d26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -18,7 +18,6 @@ package com.android.systemui.decor
import android.content.res.Resources
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
import android.view.DisplayCutout
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -32,7 +31,6 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
@SmallTest
class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
new file mode 100644
index 000000000000..621bcf69bb03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.decor
+
+import android.testing.AndroidTestingRunner
+import android.util.Size
+import android.view.DisplayCutout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.spy
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() {
+
+ @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate
+ private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory
+
+ @Before
+ fun setUp() {
+ roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null))
+ }
+
+ @Test
+ fun testNoRoundedCorners() {
+ Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+ Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize
+ Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+ roundedCornerDecorProviderFactory =
+ RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+ Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders)
+ Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size)
+ }
+
+ @Test
+ fun testHasRoundedCornersIfTopWidthLargerThan0() {
+ Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+ Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize
+ Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+ roundedCornerDecorProviderFactory =
+ RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+ Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+ roundedCornerDecorProviderFactory.providers.let { providers ->
+ Assert.assertEquals(2, providers.size)
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_top_left)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+ })
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_top_right)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+ })
+ }
+ }
+
+ @Test
+ fun testHasRoundedCornersIfBottomWidthLargerThan0() {
+ Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+ Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize
+ Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+ roundedCornerDecorProviderFactory =
+ RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+ Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+ roundedCornerDecorProviderFactory.providers.let { providers ->
+ Assert.assertEquals(2, providers.size)
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_bottom_left)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+ })
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_bottom_right)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+ })
+ }
+ }
+
+ @Test
+ fun test4CornerDecorProvidersInfo() {
+ Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize
+ Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize
+ Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius
+
+ roundedCornerDecorProviderFactory =
+ RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+ Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+ roundedCornerDecorProviderFactory.providers.let { providers ->
+ Assert.assertEquals(4, providers.size)
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_top_left)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+ })
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_top_right)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+ })
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_bottom_left)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+ })
+ Assert.assertEquals(1, providers.count {
+ ((it.viewId == R.id.rounded_corner_bottom_right)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+ and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+ })
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index b536bfdb944e..e89ed309a1b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -45,7 +45,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
}
@Test
- fun testReloadAllAndDefaultRadius() {
+ fun testUpdateDisplayUniqueId() {
mContext.orCreateTestableResources.addOverrides(
mockTypeArray = mockTypedArray,
radius = 3,
@@ -65,7 +65,34 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
radiusTop = 6,
radiusBottom = 0)
- roundedCornerResDelegate.reloadAll("test")
+ roundedCornerResDelegate.updateDisplayUniqueId("test", null)
+
+ assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
+ }
+
+ @Test
+ fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() {
+ mContext.orCreateTestableResources.addOverrides(
+ mockTypeArray = mockTypedArray,
+ radius = 3,
+ radiusTop = 0,
+ radiusBottom = 4,
+ multipleRadius = false)
+
+ roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
+
+ assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
+ assertEquals(false, roundedCornerResDelegate.isMultipleRadius)
+
+ mContext.orCreateTestableResources.addOverrides(
+ mockTypeArray = mockTypedArray,
+ radius = 5,
+ radiusTop = 6,
+ radiusBottom = 0)
+
+ roundedCornerResDelegate.updateDisplayUniqueId(null, 1)
assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
@@ -75,18 +102,24 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
fun testUpdateTuningSizeFactor() {
mContext.orCreateTestableResources.addOverrides(
mockTypeArray = mockTypedArray,
+ radius = 1,
radiusTop = 0,
- radiusBottom = 0,
+ radiusBottom = 2,
multipleRadius = false)
roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
val factor = 5
- roundedCornerResDelegate.updateTuningSizeFactor(factor)
+ roundedCornerResDelegate.updateTuningSizeFactor(factor, 1)
val length = (factor * mContext.resources.displayMetrics.density).toInt()
assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize)
assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize)
+
+ roundedCornerResDelegate.updateTuningSizeFactor(null, 2)
+
+ assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 3657192daede..f567b55b7caa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -38,6 +38,7 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.DreamPreviewComplication;
@@ -104,6 +105,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
ViewGroup mDreamOverlayContainerViewParent;
+ @Mock
+ UiEventLogger mUiEventLogger;
+
DreamOverlayService mService;
@Before
@@ -129,7 +133,22 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mDreamOverlayComponentFactory,
mStateController,
mKeyguardUpdateMonitor,
- mPreviewComplication);
+ mPreviewComplication,
+ mUiEventLogger);
+ }
+
+ @Test
+ public void testOnStartMetricsLogged() throws Exception {
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
+ verify(mUiEventLogger).log(
+ DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 6e01541c75e0..ce92d5e30455 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -39,8 +39,8 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -112,8 +112,10 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
TOUCH_REGION);
when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
+ when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
}
/**
@@ -154,7 +156,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Makes sure expansion amount is proportional to scroll.
*/
@Test
- public void testExpansionAmount() {
+ public void testExpansionAmount_whenBouncerHidden_setsCorrectValue() {
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
@@ -166,9 +168,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, SCREEN_HEIGHT_PX, 0);
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX - distanceY, 0);
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
- assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY))
.isTrue();
// Ensure only called once
@@ -180,6 +182,38 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
.onPanelExpansionChanged(eq(1 - scrollAmount), eq(false), eq(true));
}
+ /**
+ * Makes sure expansion amount is proportional to scroll.
+ */
+ @Test
+ public void testExpansionAmount_whenBouncerShown_setsCorrectValue() {
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ final float scrollAmount = .3f;
+ final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
+
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY))
+ .isTrue();
+
+ // Ensure only called once
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(anyFloat(), anyBoolean(), anyBoolean());
+
+ // Ensure correct expansion passed in.
+ verify(mStatusBarKeyguardViewManager)
+ .onPanelExpansionChanged(eq(scrollAmount), eq(false), eq(true));
+ }
+
private void swipeToPosition(float position, float velocityY) {
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
@@ -196,9 +230,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
0, SCREEN_HEIGHT_PX, 0);
final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX - distanceY, 0);
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
- assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY))
+ assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY))
.isTrue();
final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
@@ -212,14 +246,18 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* down.
*/
@Test
- public void testCollapseOnThreshold() {
+ public void testSwipeBelowThreshold_collapsesBouncer() {
final float swipeUpPercentage = .3f;
- swipeToPosition(swipeUpPercentage, -1);
-
- verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
- eq(KeyguardBouncer.EXPANSION_VISIBLE));
- verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
- anyFloat(), anyFloat());
+ final float expansion = 1 - swipeUpPercentage;
+ // The upward velocity is ignored.
+ final float velocityY = -1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator),
+ eq(SCREEN_HEIGHT_PX * expansion),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_HIDDEN),
+ eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
}
@@ -227,14 +265,59 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Tests that ending a swipe above the set expansion threshold will continue the expansion.
*/
@Test
- public void testExpandOnThreshold() {
+ public void testSwipeAboveThreshold_expandsBouncer() {
final float swipeUpPercentage = .7f;
- swipeToPosition(swipeUpPercentage, 1);
+ final float expansion = 1 - swipeUpPercentage;
+ // The downward velocity is ignored.
+ final float velocityY = 1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_VISIBLE),
+ eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
+ verify(mValueAnimator).start();
+ }
- verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage),
- eq(KeyguardBouncer.EXPANSION_HIDDEN));
- verify(mFlingAnimationUtils).apply(eq(mValueAnimator), anyFloat(), anyFloat(),
- anyFloat(), anyFloat());
+ /**
+ * Tests that swiping down with a speed above the set threshold leads to bouncer collapsing
+ * down.
+ */
+ @Test
+ public void testSwipeDownVelocityAboveMin_collapsesBouncer() {
+ when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0);
+
+ // The swipe amount above the set expansion threshold is ignored.
+ final float swipeUpPercentage = .7f;
+ final float expansion = 1 - swipeUpPercentage;
+ final float velocityY = 1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_HIDDEN));
+ verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator),
+ eq(SCREEN_HEIGHT_PX * expansion),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_HIDDEN),
+ eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
+ verify(mValueAnimator).start();
+ }
+
+ /**
+ * Tests that swiping up with a speed above the set threshold will continue the expansion.
+ */
+ @Test
+ public void testSwipeUpVelocityAboveMin_expandsBouncer() {
+ when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0);
+
+ // The swipe amount below the set expansion threshold is ignored.
+ final float swipeUpPercentage = .3f;
+ final float expansion = 1 - swipeUpPercentage;
+ final float velocityY = -1;
+ swipeToPosition(swipeUpPercentage, velocityY);
+
+ verify(mValueAnimatorCreator).create(eq(expansion), eq(KeyguardBouncer.EXPANSION_VISIBLE));
+ verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion),
+ eq(SCREEN_HEIGHT_PX * KeyguardBouncer.EXPANSION_VISIBLE),
+ eq(velocityY), eq((float) SCREEN_HEIGHT_PX));
verify(mValueAnimator).start();
}
}
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 1484c9d11ba6..fcfef4a44128 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -52,8 +52,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock
private lateinit var configurationController: ConfigurationController
- @Mock
- private lateinit var mediaFlags: MediaFlags
@Mock
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
@@ -73,15 +71,13 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
.thenReturn(true)
whenever(mediaHost.hostView).thenReturn(hostView)
hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
keyguardMediaController = KeyguardMediaController(
mediaHost,
bypassController,
statusBarStateController,
notificationLockscreenUserManager,
context,
- configurationController,
- mediaFlags
+ configurationController
)
keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
keyguardMediaController.useSplitShade = false
@@ -157,22 +153,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
}
@Test
- fun testNotificationLayout_collapsedPlayer() {
- verify(mediaHost).expansion = MediaHostState.COLLAPSED
- }
-
- @Test
- fun testSessionLayout_expandedPlayer() {
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
- keyguardMediaController = KeyguardMediaController(
- mediaHost,
- bypassController,
- statusBarStateController,
- notificationLockscreenUserManager,
- context,
- configurationController,
- mediaFlags
- )
+ fun testMediaHost_expandedPlayer() {
verify(mediaHost).expansion = MediaHostState.EXPANDED
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 90eff1ae9804..1c7e3a4e90cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -89,8 +89,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastSender: BroadcastSender
- @Mock private lateinit var holder: PlayerViewHolder
- @Mock private lateinit var sessionHolder: PlayerSessionViewHolder
+ @Mock private lateinit var viewHolder: MediaViewHolder
@Mock private lateinit var view: TransitionLayout
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
@@ -123,8 +122,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var actionPrev: ImageButton
@Mock private lateinit var longPressText: TextView
@Mock private lateinit var handler: Handler
- private lateinit var settings: View
- private lateinit var settingsText: TextView
+ private lateinit var settings: ImageButton
private lateinit var cancel: View
private lateinit var cancelText: TextView
private lateinit var dismiss: FrameLayout
@@ -162,8 +160,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
seekBar = SeekBar(context)
elapsedTimeView = TextView(context)
totalTimeView = TextView(context)
- settings = View(context)
- settingsText = TextView(context)
+ settings = ImageButton(context)
cancel = View(context)
cancelText = TextView(context)
dismiss = FrameLayout(context)
@@ -179,8 +176,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
actionPrev = ImageButton(context).also { it.setId(R.id.actionPrev) }
actionNext = ImageButton(context).also { it.setId(R.id.actionNext) }
- initPlayerHolderMocks()
- initSessionHolderMocks()
+ initMediaViewHolderMocks()
// Create media session
val metadataBuilder = MediaMetadata.Builder().apply {
@@ -217,9 +213,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
/**
- * Initialize elements common to both view holders
+ * Initialize elements in media view holder
*/
- private fun initMediaViewHolderMocks(viewHolder: MediaViewHolder) {
+ private fun initMediaViewHolderMocks() {
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
whenever(viewHolder.albumView).thenReturn(albumView)
@@ -233,6 +229,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.seekBar).thenReturn(seekBar)
// Action buttons
+ whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
+ whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
+ whenever(viewHolder.actionNext).thenReturn(actionNext)
+ whenever(viewHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
+ whenever(viewHolder.actionPrev).thenReturn(actionPrev)
+ whenever(viewHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
whenever(viewHolder.action0).thenReturn(action0)
whenever(viewHolder.getAction(R.id.action0)).thenReturn(action0)
whenever(viewHolder.action1).thenReturn(action1)
@@ -248,34 +250,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.longPressText).thenReturn(longPressText)
whenever(longPressText.handler).thenReturn(handler)
whenever(viewHolder.settings).thenReturn(settings)
- whenever(viewHolder.settingsText).thenReturn(settingsText)
whenever(viewHolder.cancel).thenReturn(cancel)
whenever(viewHolder.cancelText).thenReturn(cancelText)
whenever(viewHolder.dismiss).thenReturn(dismiss)
whenever(viewHolder.dismissText).thenReturn(dismissText)
}
- /** Mock view holder for the notification player */
- private fun initPlayerHolderMocks() {
- initMediaViewHolderMocks(holder)
-
- whenever(holder.elapsedTimeView).thenReturn(elapsedTimeView)
- whenever(holder.totalTimeView).thenReturn(totalTimeView)
- }
-
- /** Mock view holder for session player */
- private fun initSessionHolderMocks() {
- initMediaViewHolderMocks(sessionHolder)
-
- // Semantic action buttons
- whenever(sessionHolder.actionPlayPause).thenReturn(actionPlayPause)
- whenever(sessionHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
- whenever(sessionHolder.actionNext).thenReturn(actionNext)
- whenever(sessionHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
- whenever(sessionHolder.actionPrev).thenReturn(actionPrev)
- whenever(sessionHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
- }
-
@After
fun tearDown() {
session.release()
@@ -290,41 +270,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun bindSemanticActionsOldLayout() {
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
- val semanticActions = MediaButton(
- playOrPause = MediaAction(icon, Runnable {}, "play"),
- nextOrCustom = MediaAction(icon, Runnable {}, "next"),
- custom0 = MediaAction(icon, null, "custom 0"),
- custom1 = MediaAction(icon, null, "custom 1")
- )
- val state = mediaData.copy(semanticActions = semanticActions)
-
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
- player.bindPlayer(state, PACKAGE)
-
- verify(expandedSet).setVisibility(R.id.action0, ConstraintSet.VISIBLE)
- assertThat(action0.contentDescription).isEqualTo("custom 0")
- assertThat(action0.isEnabled()).isFalse()
-
- verify(expandedSet).setVisibility(R.id.action1, ConstraintSet.INVISIBLE)
- assertThat(action1.isEnabled()).isFalse()
-
- verify(expandedSet).setVisibility(R.id.action2, ConstraintSet.VISIBLE)
- assertThat(action2.isEnabled()).isTrue()
- assertThat(action2.contentDescription).isEqualTo("play")
-
- verify(expandedSet).setVisibility(R.id.action3, ConstraintSet.VISIBLE)
- assertThat(action3.isEnabled()).isTrue()
- assertThat(action3.contentDescription).isEqualTo("next")
-
- verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.VISIBLE)
- assertThat(action4.contentDescription).isEqualTo("custom 1")
- assertThat(action4.isEnabled()).isFalse()
- }
-
- @Test
- fun bindSemanticActionsNewLayout() {
+ fun bindSemanticActions() {
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
val semanticActions = MediaButton(
playOrPause = MediaAction(icon, Runnable {}, "play"),
@@ -334,7 +280,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
)
val state = mediaData.copy(semanticActions = semanticActions)
- player.attachPlayer(sessionHolder, MediaViewController.TYPE.PLAYER_SESSION)
+ player.attachPlayer(viewHolder)
player.bindPlayer(state, PACKAGE)
assertThat(actionPrev.isEnabled()).isFalse()
@@ -370,7 +316,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun bindNotificationActionsNewLayout() {
+ fun bindNotificationActions() {
val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
val actions = listOf(
MediaAction(icon, Runnable {}, "previous"),
@@ -383,7 +329,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
actionsToShowInCompact = listOf(1, 2),
semanticActions = null)
- player.attachPlayer(sessionHolder, MediaViewController.TYPE.PLAYER_SESSION)
+ player.attachPlayer(viewHolder)
player.bindPlayer(state, PACKAGE)
// Verify semantic actions are hidden
@@ -420,7 +366,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindText() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, PACKAGE)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
@@ -428,7 +374,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindDevice() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.contentDescription).isEqualTo(DEVICE_NAME)
@@ -438,7 +384,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindDisabledDevice() {
seamless.id = 1
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(device = disabledDevice)
player.bindPlayer(state, PACKAGE)
assertThat(seamless.isEnabled()).isFalse()
@@ -449,7 +395,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindNullDevice() {
val fallbackString = context.getResources().getString(R.string.media_seamless_other_device)
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(device = null)
player.bindPlayer(state, PACKAGE)
assertThat(seamless.isEnabled()).isTrue()
@@ -459,7 +405,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindDeviceResumptionPlayer() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(resumption = true)
player.bindPlayer(state, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
@@ -468,32 +414,32 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun longClick_gutsClosed() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
whenever(mediaViewController.isGutsVisible).thenReturn(false)
val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(holder.player).setOnLongClickListener(captor.capture())
+ verify(viewHolder.player).setOnLongClickListener(captor.capture())
- captor.value.onLongClick(holder.player)
+ captor.value.onLongClick(viewHolder.player)
verify(mediaViewController).openGuts()
}
@Test
fun longClick_gutsOpen() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
whenever(mediaViewController.isGutsVisible).thenReturn(true)
val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
- verify(holder.player).setOnLongClickListener(captor.capture())
+ verify(viewHolder.player).setOnLongClickListener(captor.capture())
- captor.value.onLongClick(holder.player)
+ captor.value.onLongClick(viewHolder.player)
verify(mediaViewController, never()).openGuts()
verify(mediaViewController).closeGuts(false)
}
@Test
fun cancelButtonClick_animation() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
cancel.callOnClick()
@@ -502,7 +448,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun settingsButtonClick() {
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
settings.callOnClick()
@@ -515,7 +461,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun dismissButtonClick() {
val mediaKey = "key for dismissal"
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(notificationKey = KEY)
player.bindPlayer(state, mediaKey)
@@ -527,7 +473,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun dismissButtonDisabled() {
val mediaKey = "key for dismissal"
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(isClearable = false, notificationKey = KEY)
player.bindPlayer(state, mediaKey)
@@ -539,7 +485,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
val mediaKey = "key for dismissal"
whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
- player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
+ player.attachPlayer(viewHolder)
val state = mediaData.copy(notificationKey = KEY)
player.bindPlayer(state, mediaKey)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt
deleted file mode 100644
index d6849bf2aa39..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/PlayerViewHolderTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.FrameLayout
-
-import androidx.test.filters.SmallTest
-
-import com.android.systemui.SysuiTestCase
-import com.google.common.truth.Truth.assertThat
-
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Tests for PlayerViewHolder.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class PlayerViewHolderTest : SysuiTestCase() {
-
- private lateinit var inflater: LayoutInflater
- private lateinit var parent: ViewGroup
-
- @Before
- fun setUp() {
- inflater = LayoutInflater.from(context)
- parent = FrameLayout(context)
- }
-
- @Test
- fun create() {
- val holder = PlayerViewHolder.create(inflater, parent)
- assertThat(holder.player).isNotNull()
- }
-
- @Test
- fun backgroundIsIlluminationDrawable() {
- val holder = PlayerViewHolder.create(inflater, parent)
- assertThat(holder.player.background as IlluminationDrawable).isNotNull()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index 7ac15125ea7e..99901a0cd0b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -43,7 +43,7 @@ class SeekBarObserverTest : SysuiTestCase() {
private val enabledHeight = 2
private lateinit var observer: SeekBarObserver
- @Mock private lateinit var mockHolder: PlayerViewHolder
+ @Mock private lateinit var mockHolder: MediaViewHolder
@Mock private lateinit var mockSquigglyProgress: SquigglyProgress
private lateinit var seekBarView: SeekBar
private lateinit var elapsedTimeView: TextView
@@ -64,10 +64,8 @@ class SeekBarObserverTest : SysuiTestCase() {
elapsedTimeView = TextView(context)
totalTimeView = TextView(context)
whenever(mockHolder.seekBar).thenReturn(seekBarView)
- whenever(mockHolder.elapsedTimeView).thenReturn(elapsedTimeView)
- whenever(mockHolder.totalTimeView).thenReturn(totalTimeView)
- observer = SeekBarObserver(mockHolder, false /* useSessionLayout */)
+ observer = SeekBarObserver(mockHolder)
}
@Test
@@ -79,8 +77,6 @@ class SeekBarObserverTest : SysuiTestCase() {
// THEN seek bar shows just a thin line with no text
assertThat(seekBarView.isEnabled()).isFalse()
assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0)
- assertThat(elapsedTimeView.getText()).isEqualTo("")
- assertThat(totalTimeView.getText()).isEqualTo("")
assertThat(seekBarView.contentDescription).isEqualTo("")
assertThat(seekBarView.maxHeight).isEqualTo(disabledHeight)
}
@@ -93,8 +89,6 @@ class SeekBarObserverTest : SysuiTestCase() {
observer.onChanged(data)
// THEN seek bar is visible and thick
assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE)
- assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.VISIBLE)
- assertThat(totalTimeView.getVisibility()).isEqualTo(View.VISIBLE)
assertThat(seekBarView.maxHeight).isEqualTo(enabledHeight)
}
@@ -106,8 +100,6 @@ class SeekBarObserverTest : SysuiTestCase() {
// THEN seek bar shows the progress
assertThat(seekBarView.progress).isEqualTo(3000)
assertThat(seekBarView.max).isEqualTo(120000)
- assertThat(elapsedTimeView.getText()).isEqualTo("00:03")
- assertThat(totalTimeView.getText()).isEqualTo("02:00")
val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
assertThat(seekBarView.contentDescription).isEqualTo(desc)
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 6df56e98783c..51088b1d929b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -123,7 +123,7 @@ public class ColorSchemeTest extends SysuiTestCase {
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);
+ Assert.assertTrue(cam.getChroma() <= 8.0);
}
@Test
@@ -133,7 +133,7 @@ public class ColorSchemeTest extends SysuiTestCase {
Style.EXPRESSIVE /* style */);
int neutralMid = colorScheme.getNeutral1().get(colorScheme.getNeutral1().size() / 2);
Cam cam = Cam.fromInt(neutralMid);
- Assert.assertEquals(cam.getChroma(), 12.0, 1.0);
+ Assert.assertTrue(cam.getChroma() <= 8.0);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index bb42c1277b90..4e3bdea4edfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -75,7 +75,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@Mock
private CommandQueue mCommandQueue;
@Mock
- private NavigationBar.Factory mNavigationBarFactory;
+ private NavigationBarComponent.Factory mNavigationBarFactory;
@Before
public void setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index a0aa267e850e..f5b006d732fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -29,8 +29,6 @@ import static android.view.WindowInsets.Type.ime;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -54,7 +52,6 @@ import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -82,26 +79,31 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
+import com.android.systemui.navigationbar.buttons.DeadZone;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -122,12 +124,34 @@ public class NavigationBarTest extends SysuiTestCase {
private SysuiTestableContext mSysuiTestableContextExternal;
@Mock
+ NavigationBarFrame mNavigationBarFrame;
+ @Mock
+ NavigationBarView mNavigationBarView;
+ @Mock
+ ButtonDispatcher mHomeButton;
+ @Mock
+ ButtonDispatcher mRecentsButton;
+ @Mock
+ ButtonDispatcher mAccessibilityButton;
+ @Mock
+ ButtonDispatcher mImeSwitchButton;
+ @Mock
+ ButtonDispatcher mBackButton;
+ @Mock
+ NavigationBarTransitions mNavigationBarTransitions;
+ @Mock
+ RotationButtonController mRotationButtonController;
+ @Mock
+ LightBarTransitionsController mLightBarTransitionsController;
+ @Mock
private SystemActions mSystemActions;
@Mock
private OverviewProxyService mOverviewProxyService;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock
private NavigationModeController mNavigationModeController;
@Mock
private CommandQueue mCommandQueue;
@@ -160,7 +184,10 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private AssistManager mAssistManager;
@Mock
+ private DeadZone mDeadZone;
+ @Mock
private CentralSurfaces mCentralSurfaces;
+ private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -173,6 +200,17 @@ public class NavigationBarTest extends SysuiTestCase {
.thenReturn(mEdgeBackGestureHandler);
when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
+ when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
+ when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton);
+ when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton);
+ when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton);
+ when(mNavigationBarView.getBackButton()).thenReturn(mBackButton);
+ when(mNavigationBarView.getBarTransitions()).thenReturn(mNavigationBarTransitions);
+ when(mNavigationBarView.getRotationButtonController())
+ .thenReturn(mRotationButtonController);
+ when(mNavigationBarView.getLightTransitionsController())
+ .thenReturn(mLightBarTransitionsController);
+ when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
setupSysuiDependency();
// This class inflates views that call Dependency.get, thus these injections are still
// necessary.
@@ -197,12 +235,6 @@ public class NavigationBarTest extends SysuiTestCase {
});
}
- @After
- public void tearDown() throws Exception {
- DeviceConfig.resetToDefaults(
- Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
- }
-
private void setupSysuiDependency() {
Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -228,8 +260,8 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testHomeLongPress() {
- mNavigationBar.onViewAttachedToWindow(mNavigationBar
- .createView(null, /* initialVisibility= */ true));
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
mNavigationBar.onHomeLongClick(mNavigationBar.getView());
verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
@@ -237,13 +269,14 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testHomeLongPressWithCustomDuration() throws Exception {
- DeviceConfig.setProperties(
- new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
- .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
- .build());
+ mDeviceConfigProxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ HOME_BUTTON_LONG_PRESS_DURATION_MS,
+ "100",
+ false);
when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
- mNavigationBar.onViewAttachedToWindow(mNavigationBar
- .createView(null, /* initialVisibility= */ true));
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
/*downTime=*/SystemClock.uptimeMillis(),
@@ -265,8 +298,8 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testRegisteredWithDispatcher() {
- mNavigationBar.onViewAttachedToWindow(mNavigationBar
- .createView(null, /* initialVisibility= */ true));
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
verify(mBroadcastDispatcher).registerReceiverWithHandler(
any(BroadcastReceiver.class),
any(IntentFilter.class),
@@ -286,8 +319,8 @@ public class NavigationBarTest extends SysuiTestCase {
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(defaultNavBar).checkNavBarModes();
doNothing().when(externalNavBar).checkNavBarModes();
- defaultNavBar.createView(null, /* initialVisibility= */ true);
- externalNavBar.createView(null, /* initialVisibility= */ true);
+ defaultNavBar.init();
+ externalNavBar.init();
defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
@@ -321,7 +354,7 @@ public class NavigationBarTest extends SysuiTestCase {
doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView();
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(mNavigationBar).checkNavBarModes();
- mNavigationBar.createView(null, /* initialVisibility= */ true);
+ mNavigationBar.init();
WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
@@ -357,11 +390,11 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testA11yEventAfterDetach() {
- View v = mNavigationBar.createView(null, /* initialVisibility= */ true);
- mNavigationBar.onViewAttachedToWindow(v);
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
verify(mNavBarHelper).registerNavTaskStateUpdater(any(
NavBarHelper.NavbarTaskbarStateUpdater.class));
- mNavigationBar.onViewDetachedFromWindow(v);
+ mNavigationBar.onViewDetached();
verify(mNavBarHelper).removeNavTaskStateUpdater(any(
NavBarHelper.NavbarTaskbarStateUpdater.class));
@@ -371,23 +404,31 @@ public class NavigationBarTest extends SysuiTestCase {
@Test
public void testCreateView_initiallyVisible_viewIsVisible() {
- mNavigationBar.createView(null, /* initialVisibility= */ true);
+ when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
- assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mNavigationBarView).setVisibility(View.VISIBLE);
}
@Test
public void testCreateView_initiallyNotVisible_viewIsNotVisible() {
- mNavigationBar.createView(null, /* initialVisibility= */ false);
+ when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(false);
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
- assertThat(mNavigationBar.getView().getVisibility()).isEqualTo(View.INVISIBLE);
+ verify(mNavigationBarView).setVisibility(View.INVISIBLE);
}
private NavigationBar createNavBar(Context context) {
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
when(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- return spy(new NavigationBar(context,
+ return spy(new NavigationBar(
+ mNavigationBarView,
+ mNavigationBarFrame,
+ null,
+ context,
mWindowManager,
() -> mAssistManager,
mock(AccessibilityManager.class),
@@ -396,6 +437,7 @@ public class NavigationBarTest extends SysuiTestCase {
mOverviewProxyService,
mNavigationModeController,
mStatusBarStateController,
+ mStatusBarKeyguardViewManager,
mMockSysUiState,
mBroadcastDispatcher,
mCommandQueue,
@@ -415,6 +457,8 @@ public class NavigationBarTest extends SysuiTestCase {
mAutoHideControllerFactory,
Optional.of(mTelecomManager),
mInputMethodManager,
+ mDeadZone,
+ mDeviceConfigProxyFake,
Optional.of(mock(BackAnimation.class))));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
new file mode 100644
index 000000000000..bf82e90e88f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
@@ -0,0 +1,80 @@
+package com.android.systemui.qs
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.customize.QSCustomizer
+import com.android.systemui.util.mockito.eq
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class QSContainerImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var quickStatusBarHeader: QuickStatusBarHeader
+ @Mock
+ private lateinit var qsCustomizer: QSCustomizer
+ @Mock
+ private lateinit var qsPanelContainer: NonInterceptingScrollView
+ @Mock
+ private lateinit var qsPanelController: QSPanelController
+ @Mock
+ private lateinit var quickStatusBarHeaderController: QuickStatusBarHeaderController
+
+ private lateinit var qsContainer: QSContainerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ qsContainer = QSContainerImpl(mContext, null)
+
+ setUpMockView(quickStatusBarHeader, R.id.header)
+ setUpMockView(qsCustomizer, R.id.qs_customize)
+ setUpMockView(qsPanelContainer, R.id.expanded_qs_scroll_view)
+
+ qsContainer.onFinishInflate()
+ }
+
+ private fun setUpMockView(view: View, id: Int) {
+ whenever(view.findViewById<View>(id)).thenReturn(view)
+ whenever(view.layoutParams).thenReturn(FrameLayout.LayoutParams(0, 0))
+ qsContainer.addView(view)
+ }
+
+ @Test
+ fun testContainerBottomPadding() {
+ qsContainer.updateResources(
+ qsPanelController,
+ quickStatusBarHeaderController,
+ /* newFooter */ false
+ )
+ verify(qsPanelContainer).setPaddingRelative(anyInt(), anyInt(), anyInt(), eq(0))
+
+ qsContainer.updateResources(
+ qsPanelController,
+ quickStatusBarHeaderController,
+ /* newFooter */ true
+ )
+ verify(qsPanelContainer)
+ .setPaddingRelative(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
+ )
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index ac1e86f58f86..1b48a16740b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -8,7 +8,6 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.media.MediaFlags
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.FalsingManager
@@ -49,7 +48,6 @@ class QSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var brightnessSliderFactory: BrightnessSliderController.Factory
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var featureFlags: FeatureFlags
- @Mock private lateinit var mediaFlags: MediaFlags
@Mock private lateinit var mediaHost: MediaHost
private lateinit var controller: QSPanelController
@@ -79,8 +77,7 @@ class QSPanelControllerTest : SysuiTestCase() {
brightnessControllerFactory,
brightnessSliderFactory,
falsingManager,
- featureFlags,
- mediaFlags
+ featureFlags
)
}
@@ -90,45 +87,12 @@ class QSPanelControllerTest : SysuiTestCase() {
}
@Test
- fun onInit_notSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
- setSplitShadeEnabled(false)
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
-
+ fun onInit_setsMediaAsExpanded() {
controller.onInit()
verify(mediaHost).expansion = MediaHostState.EXPANDED
}
- @Test
- fun onInit_notSplitShade_newMediaLayoutNotAvailable_setsMediaAsExpanded() {
- setSplitShadeEnabled(false)
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
-
- controller.onInit()
-
- verify(mediaHost).expansion = MediaHostState.EXPANDED
- }
-
- @Test
- fun onInit_inSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
- setSplitShadeEnabled(true)
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
-
- controller.onInit()
-
- verify(mediaHost).expansion = MediaHostState.EXPANDED
- }
-
- @Test
- fun onInit_inSplitShade_newMediaLayoutNotAvailable_setsMediaAsCollapsed() {
- setSplitShadeEnabled(true)
- whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
-
- controller.onInit()
-
- verify(mediaHost).expansion = MediaHostState.COLLAPSED
- }
-
private fun setSplitShadeEnabled(enabled: Boolean) {
mContext.orCreateTestableResources
.addOverride(R.bool.config_use_split_notification_shade, enabled)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 5213a30cfd59..04bbd60b8d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -137,20 +137,6 @@ class QSPanelTest : SysuiTestCase() {
assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
}
- @Test
- fun testBottomPadding() {
- mQsPanel.setUseNewFooter(false)
-
- mQsPanel.updatePadding()
- assertThat(mQsPanel.paddingBottom).isEqualTo(0)
-
- mQsPanel.setUseNewFooter(true)
-
- mQsPanel.updatePadding()
- assertThat(mQsPanel.paddingBottom)
- .isEqualTo(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
- }
-
private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
context.resources.configuration.apply { orientation = newOrientation }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 62915b8ac7c9..1f28210acc64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,7 +23,6 @@ import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaFlags
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.qs.QSTile
@@ -58,8 +57,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
@Mock
private lateinit var mediaHost: MediaHost
@Mock
- private lateinit var mediaFlags: MediaFlags
- @Mock
private lateinit var metricsLogger: MetricsLogger
private val uiEventLogger = UiEventLoggerFake()
@Mock
@@ -85,7 +82,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
`when`(quickQSPanel.dumpableTag).thenReturn("")
`when`(quickQSPanel.resources).thenReturn(mContext.resources)
`when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
- `when`(mediaFlags.useMediaSessionLayout()).thenReturn(false)
controller = TestQuickQSPanelController(
quickQSPanel,
@@ -94,7 +90,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
false,
mediaHost,
true,
- mediaFlags,
metricsLogger,
uiEventLogger,
qsLogger,
@@ -131,20 +126,17 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
@Test
fun testMediaExpansionUpdatedWhenConfigurationChanged() {
- `when`(mediaFlags.useMediaSessionLayout()).thenReturn(true)
-
// times(2) because both controller and base controller are registering their listeners
verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
- captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
+ // verify that media starts in the expanded state by default
verify(mediaHost).expansion = MediaHostState.EXPANDED
// Rotate device, verify media size updated
controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
- // times(2) because init will have set to collapsed because the flag was off
- verify(mediaHost, times(2)).expansion = MediaHostState.COLLAPSED
+ verify(mediaHost).expansion = MediaHostState.COLLAPSED
}
class TestQuickQSPanelController(
@@ -154,13 +146,12 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
usingMediaPlayer: Boolean,
mediaHost: MediaHost,
usingCollapsedLandscapeMedia: Boolean,
- mediaFlags: MediaFlags,
metricsLogger: MetricsLogger,
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
dumpManager: DumpManager
) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer,
- mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger,
+ mediaHost, usingCollapsedLandscapeMedia, metricsLogger, uiEventLogger, qsLogger,
dumpManager) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index de9ea27e7c13..32d625c707cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -22,84 +22,238 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry;
+import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.argThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.spy;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
import android.os.Handler;
import android.os.UserHandle;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.CoreStartable;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.os.FakeHandler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
private static final int NOTIF_USER_ID = 0;
private static final int CURR_USER_ID = 1;
- @Mock
- private Handler mMainHandler;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
- @Mock private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
+ private final FakeSettings mFakeSettings = new FakeSettings();
+ private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private NotificationEntry mEntry;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- // TODO refactor the test of KeyguardNotificationVisibilityProvider out
- mKeyguardNotificationVisibilityProvider = spy(new KeyguardNotificationVisibilityProvider(
- mContext,
- mMainHandler,
- mKeyguardStateController,
- mLockscreenUserManager,
- mKeyguardUpdateMonitor,
- mHighPriorityProvider,
- mStatusBarStateController,
- mBroadcastDispatcher
- ));
-
+ TestComponent component =
+ DaggerKeyguardNotificationVisibilityProviderTest_TestComponent
+ .factory()
+ .create(
+ mContext,
+ new FakeHandler(TestableLooper.get(this).getLooper()),
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mKeyguardUpdateMonitor,
+ mHighPriorityProvider,
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mFakeSettings,
+ mFakeSettings);
+ mKeyguardNotificationVisibilityProvider = component.getProvider();
+ for (CoreStartable startable : component.getCoreStartables().values()) {
+ startable.start();
+ }
mEntry = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID))
.build();
}
@Test
+ public void notifyListeners_onUnlockedChanged() {
+ ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+ verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+ KeyguardStateController.Callback callback = callbackCaptor.getValue();
+
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ callback.onUnlockedChanged();
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onKeyguardShowingChanged() {
+ ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+ verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
+ KeyguardStateController.Callback callback = callbackCaptor.getValue();
+
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ callback.onKeyguardShowingChanged();
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onStrongAuthStateChanged() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture());
+ KeyguardUpdateMonitorCallback callback = callbackCaptor.getValue();
+
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ callback.onStrongAuthStateChanged(0);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onStatusBarStateChanged() {
+ ArgumentCaptor<StatusBarStateController.StateListener> callbackCaptor =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+ verify(mStatusBarStateController).addCallback(callbackCaptor.capture());
+ StatusBarStateController.StateListener callback = callbackCaptor.getValue();
+
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ callback.onStateChanged(0);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onReceiveUserSwitchBroadcast() {
+ ArgumentCaptor<BroadcastReceiver> callbackCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mBroadcastDispatcher).registerReceiver(
+ callbackCaptor.capture(),
+ argThat(intentFilter -> intentFilter.hasAction(Intent.ACTION_USER_SWITCHED)),
+ isNull(),
+ isNull(),
+ eq(Context.RECEIVER_EXPORTED));
+ BroadcastReceiver callback = callbackCaptor.getValue();
+
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ callback.onReceive(mContext, new Intent(Intent.ACTION_USER_SWITCHED));
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onSettingChange_lockScreenShowNotifs() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onSettingChange_lockScreenAllowPrivateNotifs() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onSettingChange_zenMode() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ mFakeSettings.putInt(Settings.Global.ZEN_MODE, 1);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
+ public void notifyListeners_onSettingChange_lockScreenShowSilentNotifs() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ Consumer<String> listener = mock(Consumer.class);
+ mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
+
+ mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
+
+ verify(listener).accept(anyString());
+ }
+
+ @Test
public void unfilteredState() {
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
// THEN don't filter out the entry
- assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -109,7 +263,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
when(mKeyguardStateController.isShowing()).thenReturn(false);
// THEN don't filter out the entry
- assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -121,7 +275,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -133,7 +287,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -148,7 +302,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
.thenReturn(false);
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -164,7 +318,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
.setVisibilityOverride(VISIBILITY_SECRET).build());
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -180,7 +334,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
@@ -209,7 +363,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true);
// THEN don't filter out the entry
- assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(entryWithParent));
+ assertFalse(
+ mKeyguardNotificationVisibilityProvider.shouldHideNotification(entryWithParent));
// WHEN its parent doesn't exceed threshold to show on lockscreen
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false);
@@ -218,7 +373,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
.done());
// THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(entryWithParent));
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(entryWithParent));
}
/**
@@ -259,4 +414,27 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
// notification is high priority, so it shouldn't be filtered
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
}
-}
+
+ @SysUISingleton
+ @Component(modules = { KeyguardNotificationVisibilityProviderModule.class })
+ interface TestComponent {
+ KeyguardNotificationVisibilityProvider getProvider();
+ Map<Class<?>, CoreStartable> getCoreStartables();
+
+ @Component.Factory
+ interface Factory {
+ TestComponent create(
+ @BindsInstance Context context,
+ @BindsInstance @Main Handler handler,
+ @BindsInstance KeyguardStateController keyguardStateController,
+ @BindsInstance NotificationLockscreenUserManager lockscreenUserManager,
+ @BindsInstance KeyguardUpdateMonitor keyguardUpdateMonitor,
+ @BindsInstance HighPriorityProvider highPriorityProvider,
+ @BindsInstance StatusBarStateController statusBarStateController,
+ @BindsInstance BroadcastDispatcher broadcastDispatcher,
+ @BindsInstance SecureSettings secureSettings,
+ @BindsInstance GlobalSettings globalSettings
+ );
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 6526fabefe49..9e7b6c514ca3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -18,6 +18,8 @@ import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -31,6 +33,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.function.Consumer
@@ -71,6 +74,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
private lateinit var navigationModeCallback: ModeChangedListener
private lateinit var taskbarVisibilityCallback: OverviewProxyListener
private lateinit var windowInsetsCallback: Consumer<WindowInsets>
+ private lateinit var delayableExecutor: FakeExecutor
+ private lateinit var fakeSystemClock: FakeSystemClock
@Before
fun setup() {
@@ -78,11 +83,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
mContext.ensureTestableResources()
whenever(notificationsQSContainer.context).thenReturn(mContext)
whenever(notificationsQSContainer.resources).thenReturn(mContext.resources)
+ fakeSystemClock = FakeSystemClock()
+ delayableExecutor = FakeExecutor(fakeSystemClock)
controller = NotificationsQSContainerController(
notificationsQSContainer,
navigationModeController,
overviewProxyService,
- featureFlags
+ featureFlags,
+ delayableExecutor
)
overrideResource(R.dimen.split_shade_notifications_scrim_margin_bottom, SCRIM_MARGIN)
@@ -490,13 +498,32 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
container.addView(newViewWithId(1))
container.addView(newViewWithId(View.NO_ID))
val controller = NotificationsQSContainerController(container, navigationModeController,
- overviewProxyService, featureFlags)
+ overviewProxyService, featureFlags, delayableExecutor)
controller.updateResources()
assertThat(container.getChildAt(0).id).isEqualTo(1)
assertThat(container.getChildAt(1).id).isNotEqualTo(View.NO_ID)
}
+ @Test
+ fun testWindowInsetDebounce() {
+ disableSplitShade()
+ useNewFooter(true)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = emptyInsets(),
+ applyImmediately = false)
+ fakeSystemClock.advanceTime(INSET_DEBOUNCE_MILLIS / 2)
+ windowInsetsCallback.accept(windowInsets().withStableBottom())
+
+ delayableExecutor.advanceClockToLast()
+ delayableExecutor.runAllReady()
+
+ verify(notificationsQSContainer, never()).setQSContainerPaddingBottom(0)
+ verify(notificationsQSContainer).setQSContainerPaddingBottom(STABLE_INSET_BOTTOM)
+ }
+
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}
@@ -513,12 +540,17 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
private fun given(
taskbarVisible: Boolean,
navigationMode: Int,
- insets: WindowInsets
+ insets: WindowInsets,
+ applyImmediately: Boolean = true
) {
Mockito.clearInvocations(notificationsQSContainer)
taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
navigationModeCallback.onNavigationModeChanged(navigationMode)
windowInsetsCallback.accept(insets)
+ if (applyImmediately) {
+ delayableExecutor.advanceClockToLast()
+ delayableExecutor.runAllReady()
+ }
}
fun then(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index f4f55ccefd09..29488f1ba8a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -95,6 +95,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
@Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
private KeyguardBouncer mBouncer;
@Mock
private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
@@ -120,6 +122,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(mBouncer);
when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
+ when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
mViewMediatorCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 589aa0353870..7c05c69ff3b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -14,10 +14,12 @@
package com.android.systemui.statusbar.phone;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.BroadcastReceiver;
@@ -43,7 +45,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class SystemUIDialogTest extends SysuiTestCase {
- private SystemUIDialog mDialog;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@@ -52,12 +53,11 @@ public class SystemUIDialogTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(BroadcastDispatcher.class, mBroadcastDispatcher);
-
- mDialog = new SystemUIDialog(mContext);
}
@Test
public void testRegisterReceiver() {
+ final SystemUIDialog mDialog = new SystemUIDialog(mContext);
final ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
final ArgumentCaptor<IntentFilter> intentFilterCaptor =
@@ -66,10 +66,24 @@ public class SystemUIDialogTest extends SysuiTestCase {
mDialog.show();
verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture(), eq(null), any());
-
+ assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
mDialog.dismiss();
verify(mBroadcastDispatcher).unregisterReceiver(eq(broadcastReceiverCaptor.getValue()));
}
+
+
+ @Test
+ public void testNoRegisterReceiver() {
+ final SystemUIDialog mDialog = new SystemUIDialog(mContext, false);
+
+ mDialog.show();
+ verify(mBroadcastDispatcher, never()).registerReceiver(any(), any(), eq(null), any());
+ assertTrue(mDialog.isShowing());
+
+ mDialog.dismiss();
+ verify(mBroadcastDispatcher, never()).unregisterReceiver(any());
+ assertFalse(mDialog.isShowing());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 0f1b65cb7f04..309acdf13a5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -24,6 +24,7 @@ package com.android.systemui.util.mockito
*/
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatcher
import org.mockito.Mockito
/**
@@ -44,6 +45,14 @@ fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
inline fun <reified T> any(): T = any(T::class.java)
/**
+ * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when
+ * null is returned.
+ *
+ * Generic T is nullable because implicitly bounded by Any?.
+ */
+fun <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher)
+
+/**
* Kotlin type-inferred version of Mockito.nullable()
*/
inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index e251bcc39f32..c637045589e7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -18,7 +18,6 @@ package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_BY_ADMIN;
import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
-import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -31,9 +30,7 @@ import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -42,14 +39,12 @@ import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.InputMethodInfo;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.settingslib.RestrictedLockUtils;
@@ -734,7 +729,7 @@ public class AccessibilitySecurityPolicy {
final AccessibilityServiceInfo a11yServiceInfo = boundServices.get(
i).getServiceInfo();
final ComponentName service = a11yServiceInfo.getComponentName().clone();
- if (!isA11yCategoryService(a11yServiceInfo)) {
+ if (!a11yServiceInfo.isAccessibilityTool()) {
tempNonA11yCategoryServices.add(service);
if (mNonA11yCategoryServices.contains(service)) {
mNonA11yCategoryServices.remove(service);
@@ -794,69 +789,4 @@ public class AccessibilitySecurityPolicy {
mPolicyWarningUIController.onEnabledServicesChangedLocked(userId, enabledServices);
}
-
- /**
- * Identifies whether the accessibility service is true and designed for accessibility. An
- * accessibility service is considered as accessibility category if meets all conditions below:
- * <ul>
- * <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
- * <li> is installed from the trusted install source</li>
- * </ul>
- *
- * @param serviceInfo The accessibility service's serviceInfo.
- * @return Returns true if it is a true accessibility service.
- */
- public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
- if (!serviceInfo.isAccessibilityTool()) {
- return false;
- }
- if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
- return hasTrustedSystemInstallSource(
- serviceInfo.getResolveInfo().serviceInfo.packageName);
- }
- return true;
- }
-
- /** Returns true if the {@code installedPackage} is installed from the trusted install source.
- */
- private boolean hasTrustedSystemInstallSource(String installedPackage) {
- try {
- InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
- installedPackage);
- if (installSourceInfo == null) {
- return false;
- }
- final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
- if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
- installSourcePackageName,
- 0).applicationInfo.isSystemApp()) {
- return false;
- }
- return isTrustedInstallSource(installSourcePackageName);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
- }
- return false;
- }
-
- /** Returns true if the {@code installerPackage} is a trusted install source. */
- private boolean isTrustedInstallSource(String installerPackage) {
- final String[] allowedInstallingSources = mContext.getResources().getStringArray(
- com.android.internal.R.array
- .config_accessibility_allowed_install_source);
-
- if (allowedInstallingSources.length == 0) {
- //Filters unwanted default installers if no allowed install sources.
- String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
- PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
- mCurrentUserId));
- return !TextUtils.equals(defaultInstaller, installerPackage);
- }
- for (int i = 0; i < allowedInstallingSources.length; i++) {
- if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index 5080ca277445..7c12ece472c2 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -270,7 +270,7 @@ public class PolicyWarningUIController {
final AccessibilityServiceInfo a11yServiceInfo = enabledServiceInfos.get(i);
if (componentName.flattenToShortString().equals(
a11yServiceInfo.getComponentName().flattenToShortString())) {
- if (!mAccessibilitySecurityPolicy.isA11yCategoryService(a11yServiceInfo)
+ if (!a11yServiceInfo.isAccessibilityTool()
&& !mNotifiedA11yServices.contains(componentName)) {
final CharSequence displayName =
a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index e20b15a3e807..6846b2e3e6f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1006,12 +1006,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
break;
case ACTION_POINTER_UP:
- if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
event, ACTION_UP, rawEvent, pointerIdBits, policyFlags);
- }
break;
case ACTION_UP:
if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
@@ -1146,6 +1144,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
* closet to an edge of the screen.
*/
private void computeDraggingPointerIdIfNeeded(MotionEvent event) {
+ if (event.getPointerCount() != 2) {
+ mDraggingPointerId = INVALID_POINTER_ID;
+ return;
+ }
if (mDraggingPointerId != INVALID_POINTER_ID) {
// If we have a valid pointer ID, we should be good
final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a65d5b3b94f7..312105a42b30 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1617,14 +1617,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final int providerCount = mProviders.size();
for (int i = 0; i < providerCount; i++) {
Provider provider = mProviders.get(i);
- AppWidgetProviderInfo info = provider.getInfoLocked(mContext);
final String providerPackageName = provider.id.componentName.getPackageName();
- // Ignore an invalid provider, one not matching the filter,
- // or one that isn't in the given package, if any.
- boolean inPackage = packageName == null
- || providerPackageName.equals(packageName);
- if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) {
+ // Ignore an invalid provider or one that isn't in the given package, if any.
+ boolean inPackage = packageName == null || providerPackageName.equals(packageName);
+ if (provider.zombie || !inPackage) {
+ continue;
+ }
+
+ // Ignore the ones not matching the filter.
+ AppWidgetProviderInfo info = provider.getInfoLocked(mContext);
+ if ((info.widgetCategory & categoryFilter) == 0) {
continue;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a2cfe4928249..af7dcd114066 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -38,7 +38,6 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT;
-import static android.os.storage.OnObbStateChangeListener.ERROR_INTERNAL;
import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
@@ -598,12 +597,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- /** List of crypto types.
- * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
- * corresponding commands in CommandListener.cpp */
- public static final String[] CRYPTO_TYPES
- = { "password", "default", "pattern", "pin" };
-
private final Context mContext;
private final ContentResolver mResolver;
@@ -622,18 +615,6 @@ class StorageManagerService extends IStorageManager.Stub
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
- /**
- * The size of the crypto algorithm key in bits for OBB files. Currently
- * Twofish is used which takes 128-bit keys.
- */
- private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128;
-
- /**
- * The number of times to run SHA1 in the PBKDF2 function for OBB files.
- * 1024 is reasonably secure and not too slow.
- */
- private static final int PBKDF2_HASH_ROUNDS = 1024;
-
private static final String ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY =
"anr_delay_millis";
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index ed545a6f720c..206a3109e6a8 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -32,6 +32,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.UserManagerService;
import java.io.PrintWriter;
@@ -261,13 +262,23 @@ public abstract class SystemService {
public @interface EventTypesFlag {
}
- private @EventTypesFlag int mEventType;
+ private final @EventTypesFlag int mEventType;
/** @hide */
UserCompletedEventType(@EventTypesFlag int eventType) {
mEventType = eventType;
}
+ /**
+ * Creates a new instance of {@link UserCompletedEventType}.
+ * @hide
+ */
+ @VisibleForTesting
+ public static UserCompletedEventType newUserCompletedEventTypeForTest(
+ @EventTypesFlag int eventType) {
+ return new UserCompletedEventType(eventType);
+ }
+
/** Returns whether one of the events is {@link #onUserStarting}. */
public boolean includesOnUserStarting() {
return (mEventType & EVENT_TYPE_USER_STARTING) != 0;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 2dfe94729bb8..2f84ec5489fd 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -362,11 +362,9 @@ final class UiModeManagerService extends SystemService {
getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
verifySetupWizardCompleted();
synchronized (mLock) {
- // only update if the value is actually changed
- if (updateNightModeFromSettingsLocked(getContext(), getContext().getResources(),
- to.getUserIdentifier())) {
- updateLocked(0, 0);
- }
+ updateNightModeFromSettingsLocked(getContext(), getContext().getResources(),
+ to.getUserIdentifier());
+ updateLocked(0, 0);
}
}
@@ -534,19 +532,16 @@ final class UiModeManagerService extends SystemService {
}
/**
- * Updates the night mode setting in Settings.Secure and returns if the value was successfully
- * changed.
+ * Updates the night mode setting in Settings.Secure
*
* @param context A valid context
* @param res A valid resource object
* @param userId The user to update the setting for
- * @return True if the new value is different from the old value. False otherwise.
*/
- private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
+ private void updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
if (mCarModeEnabled || mCar) {
- return false;
+ return;
}
- int oldNightMode = mNightMode;
if (mSetupWizardComplete) {
mNightMode = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE, res.getInteger(
@@ -570,8 +565,6 @@ final class UiModeManagerService extends SystemService {
Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0;
}
}
-
- return oldNightMode != mNightMode;
}
private static long toMilliSeconds(LocalTime t) {
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 15484b2d618e..41ff08309d89 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -1841,6 +1841,9 @@ public final class AppRestrictionController {
}
void handleUidInactive(int uid, boolean disabled) {
+ if (!mConstantsObserver.mBgAutoRestrictedBucket) {
+ return;
+ }
final ArrayList<Runnable> pendingTasks = mTmpRunnables;
synchronized (mSettingsLock) {
final int index = mActiveUids.indexOfKey(uid);
@@ -1863,6 +1866,9 @@ public final class AppRestrictionController {
}
void handleUidActive(int uid) {
+ if (!mConstantsObserver.mBgAutoRestrictedBucket) {
+ return;
+ }
synchronized (mSettingsLock) {
final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 309a4ff16753..a08ef4b5ab8d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -226,6 +226,7 @@ public class AudioService extends IAudioService.Stub
private final AudioSystemAdapter mAudioSystem;
private final SystemServerAdapter mSystemServer;
+ private final SettingsAdapter mSettings;
/** Debug audio mode */
protected static final boolean DEBUG_MODE = false;
@@ -876,11 +877,12 @@ public class AudioService extends IAudioService.Stub
/** @hide */
public AudioService(Context context) {
this(context, AudioSystemAdapter.getDefaultAdapter(),
- SystemServerAdapter.getDefaultAdapter(context));
+ SystemServerAdapter.getDefaultAdapter(context),
+ SettingsAdapter.getDefaultAdapter());
}
public AudioService(Context context, AudioSystemAdapter audioSystem,
- SystemServerAdapter systemServer) {
+ SystemServerAdapter systemServer, SettingsAdapter settings) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
@@ -888,6 +890,7 @@ public class AudioService extends IAudioService.Stub
mAudioSystem = audioSystem;
mSystemServer = systemServer;
+ mSettings = settings;
mPlatformType = AudioSystem.getPlatformType(context);
@@ -1024,7 +1027,7 @@ public class AudioService extends IAudioService.Stub
new String("AudioService ctor"),
0);
- mSafeMediaVolumeState = Settings.Global.getInt(mContentResolver,
+ mSafeMediaVolumeState = mSettings.getGlobalInt(mContentResolver,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
// The default safe volume index read here will be replaced by the actual value when
@@ -2009,7 +2012,7 @@ public class AudioService extends IAudioService.Stub
private void readDockAudioSettings(ContentResolver cr)
{
- mDockAudioMediaEnabled = Settings.Global.getInt(
+ mDockAudioMediaEnabled = mSettings.getGlobalInt(
cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
sendMsg(mAudioHandler,
@@ -2025,7 +2028,7 @@ public class AudioService extends IAudioService.Stub
private void updateMasterMono(ContentResolver cr)
{
- final boolean masterMono = System.getIntForUser(
+ final boolean masterMono = mSettings.getSystemIntForUser(
cr, System.MASTER_MONO, 0 /* default */, UserHandle.USER_CURRENT) == 1;
if (DEBUG_VOL) {
Log.d(TAG, String.format("Master mono %b", masterMono));
@@ -2046,7 +2049,7 @@ public class AudioService extends IAudioService.Stub
private void sendEncodedSurroundMode(ContentResolver cr, String eventSource)
{
- final int encodedSurroundMode = Settings.Global.getInt(
+ final int encodedSurroundMode = mSettings.getGlobalInt(
cr, Settings.Global.ENCODED_SURROUND_OUTPUT,
Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
sendEncodedSurroundMode(encodedSurroundMode, eventSource);
@@ -2162,7 +2165,7 @@ public class AudioService extends IAudioService.Stub
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSettingsLock) {
- Settings.Global.putString(mContentResolver,
+ mSettings.putGlobalString(mContentResolver,
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
TextUtils.join(",", enabledFormats));
}
@@ -2183,7 +2186,7 @@ public class AudioService extends IAudioService.Stub
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSettingsLock) {
- Settings.Global.putInt(mContentResolver,
+ mSettings.putGlobalInt(mContentResolver,
Settings.Global.ENCODED_SURROUND_OUTPUT,
toEncodedSurroundSetting(mode));
}
@@ -2204,7 +2207,7 @@ public class AudioService extends IAudioService.Stub
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSettingsLock) {
- int encodedSurroundSetting = Settings.Global.getInt(mContentResolver,
+ int encodedSurroundSetting = mSettings.getGlobalInt(mContentResolver,
Settings.Global.ENCODED_SURROUND_OUTPUT,
AudioManager.ENCODED_SURROUND_OUTPUT_AUTO);
return toEncodedSurroundOutputMode(encodedSurroundSetting, targetSdkVersion);
@@ -2217,7 +2220,7 @@ public class AudioService extends IAudioService.Stub
/** @return the formats that are enabled in global settings */
private HashSet<Integer> getEnabledFormats() {
HashSet<Integer> formats = new HashSet<>();
- String enabledFormats = Settings.Global.getString(mContentResolver,
+ String enabledFormats = mSettings.getGlobalString(mContentResolver,
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
if (enabledFormats != null) {
try {
@@ -2280,7 +2283,7 @@ public class AudioService extends IAudioService.Stub
// Manually enable surround formats only when the setting is in manual mode.
return;
}
- String enabledSurroundFormats = Settings.Global.getString(
+ String enabledSurroundFormats = mSettings.getGlobalString(
cr, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
if (enabledSurroundFormats == null) {
// Never allow enabledSurroundFormats as a null, which could happen when
@@ -2308,7 +2311,7 @@ public class AudioService extends IAudioService.Stub
}
// Set filtered surround formats to settings DB in case
// there are invalid surround formats in original settings.
- Settings.Global.putString(mContext.getContentResolver(),
+ mSettings.putGlobalString(mContext.getContentResolver(),
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
TextUtils.join(",", formats));
sendMsg(mAudioHandler, MSG_ENABLE_SURROUND_FORMATS, SENDMSG_QUEUE, 0, 0, formats, 0);
@@ -2336,11 +2339,11 @@ public class AudioService extends IAudioService.Stub
packageName = mRoleObserver.getAssistantRoleHolder();
}
if (TextUtils.isEmpty(packageName)) {
- String assistantName = Settings.Secure.getStringForUser(
+ String assistantName = mSettings.getSecureStringForUser(
mContentResolver,
Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
if (TextUtils.isEmpty(assistantName)) {
- assistantName = Settings.Secure.getStringForUser(
+ assistantName = mSettings.getSecureStringForUser(
mContentResolver,
Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
}
@@ -2383,7 +2386,7 @@ public class AudioService extends IAudioService.Stub
final ContentResolver cr = mContentResolver;
int ringerModeFromSettings =
- Settings.Global.getInt(
+ mSettings.getGlobalInt(
cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
int ringerMode = ringerModeFromSettings;
// validity check in case the settings are restored from a device with incompatible
@@ -2395,7 +2398,7 @@ public class AudioService extends IAudioService.Stub
ringerMode = AudioManager.RINGER_MODE_SILENT;
}
if (ringerMode != ringerModeFromSettings) {
- Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
+ mSettings.putGlobalInt(cr, Settings.Global.MODE_RINGER, ringerMode);
}
if (mUseFixedVolume || mIsSingleVolume) {
ringerMode = AudioManager.RINGER_MODE_NORMAL;
@@ -2427,7 +2430,7 @@ public class AudioService extends IAudioService.Stub
AudioSystem.setRttEnabled(mRttEnabled);
}
- mMuteAffectedStreams = System.getIntForUser(cr,
+ mMuteAffectedStreams = mSettings.getSystemIntForUser(cr,
System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
UserHandle.USER_CURRENT);
@@ -4480,7 +4483,7 @@ public class AudioService extends IAudioService.Stub
int silenceRingerSetting = Settings.Secure.VOLUME_HUSH_OFF;
if (mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
- silenceRingerSetting = Settings.Secure.getIntForUser(mContentResolver,
+ silenceRingerSetting = mSettings.getSecureIntForUser(mContentResolver,
Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
UserHandle.USER_CURRENT);
}
@@ -5241,7 +5244,7 @@ public class AudioService extends IAudioService.Stub
* Settings has an in memory cache, so this is fast.
*/
private boolean querySoundEffectsEnabled(int user) {
- return Settings.System.getIntForUser(getContentResolver(),
+ return mSettings.getSystemIntForUser(getContentResolver(),
Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
}
@@ -5326,7 +5329,7 @@ public class AudioService extends IAudioService.Stub
checkMuteAffectedStreams();
synchronized (mSafeMediaVolumeStateLock) {
- mMusicActiveMs = MathUtils.constrain(Settings.Secure.getIntForUser(mContentResolver,
+ mMusicActiveMs = MathUtils.constrain(mSettings.getSecureIntForUser(mContentResolver,
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, 0, UserHandle.USER_CURRENT),
0, UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX);
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
@@ -5968,7 +5971,7 @@ public class AudioService extends IAudioService.Stub
@GuardedBy("mSettingsLock")
private boolean updateRingerAndZenModeAffectedStreams() {
boolean updatedZenModeAffectedStreams = updateZenModeAffectedStreams();
- int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
+ int ringerModeAffectedStreams = mSettings.getSystemIntForUser(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
(1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
@@ -5992,7 +5995,7 @@ public class AudioService extends IAudioService.Stub
}
if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
- Settings.System.putIntForUser(mContentResolver,
+ mSettings.putSystemIntForUser(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
ringerModeAffectedStreams,
UserHandle.USER_CURRENT);
@@ -6969,7 +6972,7 @@ public class AudioService extends IAudioService.Stub
+ ", device " + AudioSystem.getOutputDeviceName(device)
+ " and User=" + ActivityManager.getCurrentUser());
}
- boolean success = Settings.System.putIntForUser(mContentResolver,
+ boolean success = mSettings.putSystemIntForUser(mContentResolver,
getSettingNameForDevice(device),
getIndex(device),
UserHandle.USER_CURRENT);
@@ -6995,7 +6998,7 @@ public class AudioService extends IAudioService.Stub
? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1;
int index;
String name = getSettingNameForDevice(device);
- index = Settings.System.getIntForUser(
+ index = mSettings.getSystemIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
if (index == -1) {
continue;
@@ -7244,7 +7247,7 @@ public class AudioService extends IAudioService.Stub
index = defaultIndex;
} else {
String name = getSettingNameForDevice(device);
- index = Settings.System.getIntForUser(
+ index = mSettings.getSystemIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
}
if (index == -1) {
@@ -7781,7 +7784,7 @@ public class AudioService extends IAudioService.Stub
return;
}
if (streamState.hasValidSettingsName()) {
- System.putIntForUser(mContentResolver,
+ mSettings.putSystemIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
(streamState.getIndex(device) + 5)/ 10,
UserHandle.USER_CURRENT);
@@ -7792,11 +7795,11 @@ public class AudioService extends IAudioService.Stub
if (mUseFixedVolume) {
return;
}
- Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
+ mSettings.putGlobalInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
}
private void onPersistSafeVolumeState(int state) {
- Settings.Global.putInt(mContentResolver,
+ mSettings.putGlobalInt(mContentResolver,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
state);
}
@@ -7940,7 +7943,7 @@ public class AudioService extends IAudioService.Stub
case MSG_PERSIST_MUSIC_ACTIVE_MS:
final int musicActiveMs = msg.arg1;
- Settings.Secure.putIntForUser(mContentResolver,
+ mSettings.putSecureIntForUser(mContentResolver,
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
UserHandle.USER_CURRENT);
break;
@@ -8081,12 +8084,12 @@ public class AudioService extends IAudioService.Stub
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MASTER_BALANCE), false, this);
- mEncodedSurroundMode = Settings.Global.getInt(
+ mEncodedSurroundMode = mSettings.getGlobalInt(
mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ENCODED_SURROUND_OUTPUT), false, this);
- mEnabledSurroundFormats = Settings.Global.getString(
+ mEnabledSurroundFormats = mSettings.getGlobalString(
mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
@@ -8120,7 +8123,7 @@ public class AudioService extends IAudioService.Stub
}
private void updateEncodedSurroundOutput() {
- int newSurroundMode = Settings.Global.getInt(
+ int newSurroundMode = mSettings.getGlobalInt(
mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
// Did it change?
@@ -8663,13 +8666,13 @@ public class AudioService extends IAudioService.Stub
}
void onPersistSpatialAudioEnabled(boolean enabled) {
- Settings.Secure.putIntForUser(mContentResolver,
+ mSettings.putSecureIntForUser(mContentResolver,
Settings.Secure.SPATIAL_AUDIO_ENABLED, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
boolean isSpatialAudioEnabled() {
- return Settings.Secure.getIntForUser(mContentResolver,
+ return mSettings.getSecureIntForUser(mContentResolver,
Settings.Secure.SPATIAL_AUDIO_ENABLED, SPATIAL_AUDIO_ENABLED_DEFAULT ? 1 : 0,
UserHandle.USER_CURRENT) == 1;
}
@@ -9806,7 +9809,7 @@ public class AudioService extends IAudioService.Stub
}
public void loadSettings(ContentResolver cr) {
- mLongPressTimeout = Settings.Secure.getIntForUser(cr,
+ mLongPressTimeout = mSettings.getSecureIntForUser(cr,
Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
}
@@ -11430,7 +11433,7 @@ public class AudioService extends IAudioService.Stub
}
final long callingIdentity = Binder.clearCallingIdentity();
try {
- System.putIntForUser(mContentResolver,
+ mSettings.putSystemIntForUser(mContentResolver,
getSettingsNameForDeviceVolumeBehavior(deviceType),
deviceVolumeBehavior,
UserHandle.USER_CURRENT);
@@ -11441,7 +11444,7 @@ public class AudioService extends IAudioService.Stub
@AudioManager.DeviceVolumeBehaviorState
private int retrieveStoredDeviceVolumeBehavior(int deviceType) {
- return System.getIntForUser(mContentResolver,
+ return mSettings.getSystemIntForUser(mContentResolver,
getSettingsNameForDeviceVolumeBehavior(deviceType),
AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET,
UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/SettingsAdapter.java b/services/core/java/com/android/server/audio/SettingsAdapter.java
new file mode 100644
index 000000000000..dbc4d6d1c676
--- /dev/null
+++ b/services/core/java/com/android/server/audio/SettingsAdapter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.server.audio;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Adapter for methods that read and write settings in android.provider.Settings.
+ */
+public class SettingsAdapter {
+ public static SettingsAdapter getDefaultAdapter() {
+ return new SettingsAdapter();
+ }
+
+ /**
+ * Wrapper methods for Settings.Global
+ */
+
+ /** Wraps {@link Settings.Global#getInt(ContentResolver, String, int)} */
+ public int getGlobalInt(ContentResolver cr, String name, int def) {
+ return Settings.Global.getInt(cr, name, def);
+ }
+
+ /** Wraps {@link Settings.Global#getString(ContentResolver, String)} */
+ public String getGlobalString(ContentResolver resolver, String name) {
+ return Settings.Global.getString(resolver, name);
+ }
+
+ /** Wraps {@link Settings.Global#putInt(ContentResolver, String, int)} */
+ public boolean putGlobalInt(ContentResolver cr, String name, int value) {
+ return Settings.Global.putInt(cr, name, value);
+ }
+
+ /** Wraps {@link Settings.Global#putString(ContentResolver, String, String)} */
+ public boolean putGlobalString(ContentResolver resolver, String name, String value) {
+ return Settings.Global.putString(resolver, name, value);
+ }
+
+ /**
+ * Wrapper methods for Settings.System
+ */
+
+ /** Wraps {@link Settings.System#getIntForUser(ContentResolver, String, int, int)} */
+ public int getSystemIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ return Settings.System.getIntForUser(cr, name, def, userHandle);
+ }
+
+ /** Wraps {@link Settings.System#putIntForUser(ContentResolver, String, int, int)} */
+ public boolean putSystemIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+ return Settings.System.putIntForUser(cr, name, value, userHandle);
+ }
+
+ /**
+ * Wrapper methods for Settings.Secure
+ */
+
+ /** Wraps {@link Settings.Secure#getIntForUser(ContentResolver, String, int, int)} */
+ public int getSecureIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ return Settings.Secure.getIntForUser(cr, name, def, userHandle);
+ }
+
+ /** Wraps {@link Settings.Secure#getStringForUser(ContentResolver, String, int)} */
+ public String getSecureStringForUser(ContentResolver resolver, String name, int userHandle) {
+ return Settings.Secure.getStringForUser(resolver, name, userHandle);
+ }
+
+ /** Wraps {@link Settings.Secure#putIntForUser(ContentResolver, String, int, int)} */
+ public boolean putSecureIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+ return Settings.Secure.putIntForUser(cr, name, value, userHandle);
+ }
+
+ /** Wraps {@link Settings.Secure#putStringForUser(ContentResolver, String, String, int)} */
+ public boolean putSecureStringForUser(ContentResolver cr, String name, String value,
+ int userHandle) {
+ return Settings.Secure.putStringForUser(cr, name, value, userHandle);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 108e7bcb23bb..b9efdf551646 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -71,7 +71,6 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -222,8 +221,10 @@ public class MultipathPolicyTracker {
"Can't get TelephonyManager for subId %d", mSubId));
}
- subscriberId = Objects.requireNonNull(tele.getSubscriberId(),
- "Null subscriber Id for subId " + mSubId);
+ subscriberId = tele.getSubscriberId();
+ if (subscriberId == null) {
+ throw new IllegalStateException("Null subscriber Id for subId " + mSubId);
+ }
mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
.setSubscriberIds(Set.of(subscriberId))
.setMeteredness(NetworkStats.METERED_YES)
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 9067f2e25152..f819e56e6856 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -129,7 +129,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private static final int MSG_STOP = 9;
private static final int MSG_UPDATE_BRIGHTNESS = 10;
private static final int MSG_UPDATE_RBC = 11;
- private static final int MSG_STATSD_HBM_BRIGHTNESS = 12;
+ private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
+ private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -1050,9 +1051,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void onAnimationEnd() {
sendUpdatePowerState();
-
- final float brightness = mPowerState.getScreenBrightness();
- reportStats(brightness);
+ Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE);
+ mHandler.sendMessage(msg);
}
};
@@ -1066,6 +1066,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mCallbacks.releaseSuspendBlocker();
mUnfinishedBusiness = false;
}
+
+ final float brightness = mPowerState != null
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
+ reportStats(brightness);
+
if (mPowerState != null) {
mPowerState.stop();
mPowerState = null;
@@ -2580,6 +2586,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
private void reportStats(float brightness) {
+ if (mLastStatsBrightness == brightness) {
+ return;
+ }
+
float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
synchronized(mCachedBrightnessInfo) {
if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
@@ -2680,6 +2690,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
handleRbcChanged(strengthChanged == 1, justActivated == 1);
break;
+ case MSG_BRIGHTNESS_RAMP_DONE:
+ if (mPowerState != null) {
+ final float brightness = mPowerState.getScreenBrightness();
+ reportStats(brightness);
+ }
+ break;
+
case MSG_STATSD_HBM_BRIGHTNESS:
logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
break;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index e6fd40902386..e62c5c13f04d 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -245,4 +245,13 @@ final class IInputMethodInvoker {
logRemoteException(e);
}
}
+
+ @AnyThread
+ void finishStylusHandwriting() {
+ try {
+ mTarget.finishStylusHandwriting();
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 29dcdfaa1bba..a2d3588f0e68 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -19,6 +19,7 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
@@ -150,6 +151,12 @@ public abstract class InputMethodManagerInternal {
public abstract void updateImeWindowStatus(boolean disableImeIcon);
/**
+ * Finish stylus handwriting by calling {@link InputMethodService#finishStylusHandwriting()} if
+ * there is an ongoing handwriting session.
+ */
+ public abstract void maybeFinishStylusHandwriting();
+
+ /**
* Callback when the IInputMethodSession from the accessibility service with the specified
* accessibilityConnectionId is created.
*
@@ -239,6 +246,10 @@ public abstract class InputMethodManagerInternal {
@Override
public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId) {
}
+
+ @Override
+ public void maybeFinishStylusHandwriting() {
+ }
};
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 77dcbd3e9277..d6131d162173 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -49,6 +49,7 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
+import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeStateVisibleAllowed;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -165,6 +166,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
@@ -178,6 +180,7 @@ import com.android.server.AccessibilityManagerInternal;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -187,6 +190,8 @@ import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.utils.PriorityDump;
import com.android.server.wm.WindowManagerInternal;
+import com.google.android.collect.Sets;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -201,8 +206,10 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.OptionalInt;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -230,6 +237,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private static final int MSG_RESET_HANDWRITING = 1090;
private static final int MSG_START_HANDWRITING = 1100;
+ private static final int MSG_FINISH_HANDWRITING = 1110;
private static final int MSG_UNBIND_CLIENT = 3000;
private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 3001;
@@ -267,6 +275,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
private final boolean mPreventImeStartupUnlessTextEditor;
+ /**
+ * These IMEs are known not to behave well when evicted from memory and thus are exempt
+ * from the IME startup avoidance behavior that is enabled by
+ * {@link #mPreventImeStartupUnlessTextEditor}.
+ */
+ @NonNull
+ private final Set<String> mNonPreemptibleInputMethods;
+
@UserIdInt
private int mLastSwitchUserId;
@@ -339,6 +355,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
@Nullable
private OverlayableSystemBooleanResourceWrapper mImeDrawsImeNavBarRes;
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ Future<?> mImeDrawsImeNavBarResLazyInitFuture;
static class SessionState {
final ClientState client;
@@ -1692,6 +1711,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mBindingController = new InputMethodBindingController(this);
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
+ mNonPreemptibleInputMethods = Sets.newHashSet(mRes.getStringArray(
+ com.android.internal.R.array.config_nonPreemptibleInputMethods));
mHwController = new HandwritingModeController(thread.getLooper(),
new InkWindowInitializer());
}
@@ -1728,7 +1749,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private void recreateImeDrawsImeNavBarResIfNecessary(@UserIdInt int targetUserId) {
+ private void maybeInitImeNavbarConfigLocked(@UserIdInt int targetUserId) {
// Currently, com.android.internal.R.bool.config_imeDrawsImeNavBar is overlaid only for the
// profile parent user.
// TODO(b/221443458): See if we can make OverlayManager be aware of profile groups.
@@ -1772,7 +1793,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
- recreateImeDrawsImeNavBarResIfNecessary(newUserId);
+ maybeInitImeNavbarConfigLocked(newUserId);
// ContentObserver should be registered again when the user is changed
mSettingsObserver.registerContentObserverLocked(newUserId);
@@ -1878,7 +1899,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
});
}
- recreateImeDrawsImeNavBarResIfNecessary(currentUserId);
+ // TODO(b/32343335): The entire systemRunning() method needs to be revisited.
+ mImeDrawsImeNavBarResLazyInitFuture = SystemServerInitThreadPool.submit(() -> {
+ // Note that the synchronization block below guarantees that the task
+ // can never be completed before the returned Future<?> object is assigned to
+ // the "mImeDrawsImeNavBarResLazyInitFuture" field.
+ synchronized (ImfLock.class) {
+ mImeDrawsImeNavBarResLazyInitFuture = null;
+ if (currentUserId != mSettings.getCurrentUserId()) {
+ // This means that the current user is already switched to other user
+ // before the background task is executed. In this scenario the relevant
+ // field should already be initialized.
+ return;
+ }
+ maybeInitImeNavbarConfigLocked(currentUserId);
+ }
+ }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
mSettingsObserver.registerContentObserverLocked(currentUserId);
@@ -2548,7 +2584,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
- String selectedMethodId = getSelectedMethodIdLocked();
+ final String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
return InputBindResult.NO_IME;
}
@@ -2592,10 +2628,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mCurAttribute = attribute;
// If configured, we want to avoid starting up the IME if it is not supposed to be showing
- if (mPreventImeStartupUnlessTextEditor
- && !InputMethodUtils.isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion,
- startInputFlags)
- && !mShowRequested) {
+ if (shouldPreventImeStartupLocked(selectedMethodId, startInputFlags,
+ unverifiedTargetSdkVersion)) {
if (DEBUG) {
Slog.d(TAG, "Avoiding IME startup and unbinding current input method.");
}
@@ -2637,6 +2671,34 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
+ private boolean shouldPreventImeStartupLocked(
+ @NonNull String selectedMethodId,
+ @StartInputFlags int startInputFlags,
+ int unverifiedTargetSdkVersion) {
+ // Fast-path for the majority of cases
+ if (!mPreventImeStartupUnlessTextEditor) {
+ return false;
+ }
+
+ final boolean imeVisibleAllowed =
+ isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags);
+
+ return !(imeVisibleAllowed
+ || mShowRequested
+ || isNonPreemptibleImeLocked(selectedMethodId));
+ }
+
+ /** Return {@code true} if the given IME is non-preemptible like the tv remote service. */
+ @GuardedBy("ImfLock.class")
+ private boolean isNonPreemptibleImeLocked(@NonNull String selectedMethodId) {
+ final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
+ if (imi != null) {
+ return mNonPreemptibleInputMethods.contains(imi.getPackageName());
+ }
+ return false;
+ }
+
+ @GuardedBy("ImfLock.class")
private boolean isSelectedMethodBoundLocked() {
String curId = getCurIdLocked();
return curId != null && curId.equals(getSelectedMethodIdLocked())
@@ -2988,6 +3050,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
@InputMethodNavButtonFlags
private int getInputMethodNavButtonFlagsLocked() {
+ if (mImeDrawsImeNavBarResLazyInitFuture != null) {
+ // TODO(b/225366708): Avoid Future.get(), which is internally used here.
+ ConcurrentUtils.waitForFutureNoInterrupt(mImeDrawsImeNavBarResLazyInitFuture,
+ "Waiting for the lazy init of mImeDrawsImeNavBarRes");
+ }
final boolean canImeDrawsImeNavBar =
mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get();
final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
@@ -3826,7 +3893,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
- if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ if (isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
@@ -3844,7 +3911,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
if (DEBUG) Slog.v(TAG, "Window asks to always show input");
- if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ if (isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
if (!sameWindowFocused) {
if (attribute != null) {
@@ -4430,7 +4497,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void finishStylusHandwriting(int requestId) {
+ private void resetStylusHandwriting(int requestId) {
synchronized (ImfLock.class) {
final OptionalInt curRequest = mHwController.getCurrentRequestId();
if (!curRequest.isPresent() || curRequest.getAsInt() != requestId) {
@@ -4797,6 +4864,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
return true;
+ case MSG_FINISH_HANDWRITING:
+ synchronized (ImfLock.class) {
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null && mHwController.getCurrentRequestId().isPresent()) {
+ curMethod.finishStylusHandwriting();
+ }
+ }
+ return true;
}
return false;
}
@@ -5435,6 +5510,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
}
+
+ @Override
+ public void maybeFinishStylusHandwriting() {
+ mHandler.removeMessages(MSG_FINISH_HANDWRITING);
+ mHandler.obtainMessage(MSG_FINISH_HANDWRITING).sendToTarget();
+ }
}
@BinderThread
@@ -6388,8 +6469,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void finishStylusHandwriting(int requestId) {
- mImms.finishStylusHandwriting(requestId);
+ public void resetStylusHandwriting(int requestId) {
+ mImms.resetStylusHandwriting(requestId);
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0a34eedd47cb..589b8f18b1a5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -162,6 +162,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -4539,9 +4540,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
try {
// update rules for all installed applications
- final PackageManager pm = mContext.getPackageManager();
final List<UserInfo> users;
- final List<ApplicationInfo> apps;
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-users");
try {
@@ -4549,26 +4548,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
- Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-uids");
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "iterate-uids");
try {
- apps = pm.getInstalledApplications(
- PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ final int usersSize = users.size();
+ for (int i = 0; i < usersSize; ++i) {
+ final int userId = users.get(i).id;
+ final SparseBooleanArray sharedAppIdsHandled = new SparseBooleanArray();
+ packageManagerInternal.forEachInstalledPackage(androidPackage -> {
+ final int appId = androidPackage.getUid();
+ if (androidPackage.getSharedUserId() != null) {
+ if (sharedAppIdsHandled.indexOfKey(appId) < 0) {
+ sharedAppIdsHandled.put(appId, true);
+ } else {
+ return;
+ }
+ }
+ final int uid = UserHandle.getUid(userId, appId);
+ consumer.accept(uid);
+ }, userId);
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
-
- final int usersSize = users.size();
- final int appsSize = apps.size();
- for (int i = 0; i < usersSize; i++) {
- final UserInfo user = users.get(i);
- for (int j = 0; j < appsSize; j++) {
- final ApplicationInfo app = apps.get(j);
- final int uid = UserHandle.getUid(user.id, app.uid);
- consumer.accept(uid);
- }
- }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -5451,11 +5454,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (LOGV) Slog.v(TAG, "setMeteredNetworkDenylist " + uid + ": " + enable);
try {
mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable);
- mLogger.meteredAllowlistChanged(uid, enable);
+ mLogger.meteredDenylistChanged(uid, enable);
if (Process.isApplicationUid(uid)) {
final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
mNetworkManager.setUidOnMeteredNetworkDenylist(sdkSandboxUid, enable);
- mLogger.meteredAllowlistChanged(sdkSandboxUid, enable);
+ mLogger.meteredDenylistChanged(sdkSandboxUid, enable);
}
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e);
@@ -5468,11 +5471,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (LOGV) Slog.v(TAG, "setMeteredNetworkAllowlist " + uid + ": " + enable);
try {
mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable);
- mLogger.meteredDenylistChanged(uid, enable);
+ mLogger.meteredAllowlistChanged(uid, enable);
if (Process.isApplicationUid(uid)) {
final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
mNetworkManager.setUidOnMeteredNetworkAllowlist(sdkSandboxUid, enable);
- mLogger.meteredDenylistChanged(sdkSandboxUid, enable);
+ mLogger.meteredAllowlistChanged(sdkSandboxUid, enable);
}
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 76d3d233d49a..a91c55fb895d 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -520,7 +520,7 @@ public abstract class ApexManager {
@Override
public List<ActiveApexInfo> getActiveApexInfos() {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
- Trace.TRACE_TAG_APEX_MANAGER);
+ Trace.TRACE_TAG_PACKAGE_MANAGER);
synchronized (mLock) {
if (mActiveApexInfosCache == null) {
t.traceBegin("getActiveApexInfos_noCache");
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 54a103959d04..4abfd3404295 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1429,7 +1429,8 @@ public class ComputerEngine implements Computer {
if (userId == UserHandle.USER_SYSTEM) {
return resolveInfos;
}
- for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+
+ for (int i = CollectionUtils.size(resolveInfos) - 1; i >= 0; i--) {
ResolveInfo info = resolveInfos.get(i);
if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
resolveInfos.remove(i);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 45c5116bea59..fff66629048d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -284,6 +284,17 @@ public class Installer extends SystemService {
}
/**
+ * Sets in Installd that it is first boot after data wipe
+ */
+ public void setFirstBoot() throws InstallerException {
+ try {
+ mInstalld.setFirstBoot();
+ } catch (RemoteException e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
* Class that collects multiple {@code installd} operations together in an
* attempt to more efficiently execute them in bulk.
* <p>
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index aa5f39f78cbd..002d500e4df5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -640,7 +640,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
&& params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
? params.installerPackageName : installerPackageName;
- if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
+ if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)
+ || PackageInstallerSession.isSystemDataLoaderInstallation(params)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
// adb installs can override the installingPackageName, but not the
// initiatingPackageName
@@ -666,8 +667,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
&& !mPm.isCallerVerifier(snapshot, callingUid)) {
params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
}
- if (mContext.checkCallingOrSelfPermission(
- Manifest.permission.INSTALL_TEST_ONLY_PACKAGE)
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_TEST_ONLY_PACKAGE)
!= PackageManager.PERMISSION_GRANTED) {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index beb198643e78..5ba4cc115c21 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -29,7 +29,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
-import static android.content.pm.PackageManager.INSTALL_FROM_ADB;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -705,6 +704,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
+ static boolean isDataLoaderInstallation(SessionParams params) {
+ return params.dataLoaderParams != null;
+ }
+
+ static boolean isSystemDataLoaderInstallation(SessionParams params) {
+ if (!isDataLoaderInstallation(params)) {
+ return false;
+ }
+ return SYSTEM_DATA_LOADER_PACKAGE.equals(
+ params.dataLoaderParams.getComponentName().getPackageName());
+ }
+
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@@ -744,7 +755,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
};
private boolean isDataLoaderInstallation() {
- return params.dataLoaderParams != null;
+ return isDataLoaderInstallation(this.params);
}
private boolean isStreamingInstallation() {
@@ -756,11 +767,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private boolean isSystemDataLoaderInstallation() {
- if (!isDataLoaderInstallation()) {
- return false;
- }
- return SYSTEM_DATA_LOADER_PACKAGE.equals(
- this.params.dataLoaderParams.getComponentName().getPackageName());
+ return isSystemDataLoaderInstallation(this.params);
}
/**
@@ -957,17 +964,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"DataLoader installation of APEX modules is not allowed.");
}
- if (isSystemDataLoaderInstallation()) {
- if (mContext.checkCallingOrSelfPermission(
- Manifest.permission.USE_SYSTEM_DATA_LOADERS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission "
- + "to use system data loaders");
- }
-
- // All installations using system dataloaders marked as ADB.
- this.params.installFlags |= INSTALL_FROM_ADB;
+ if (isSystemDataLoaderInstallation() && mContext.checkCallingOrSelfPermission(
+ Manifest.permission.USE_SYSTEM_DATA_LOADERS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need the "
+ + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission "
+ + "to use system data loaders");
}
}
@@ -1264,13 +1266,21 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- final String initiatingPackageName = getInstallSource().initiatingPackageName;
+ final String installerPackageName;
+ if (!TextUtils.isEmpty(getInstallSource().initiatingPackageName)) {
+ installerPackageName = getInstallSource().initiatingPackageName;
+ } else {
+ installerPackageName = getInstallSource().installerPackageName;
+ }
+ if (TextUtils.isEmpty(installerPackageName)) {
+ throw new IllegalStateException("Installer package is empty.");
+ }
final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- appOps.checkPackage(Binder.getCallingUid(), initiatingPackageName);
+ appOps.checkPackage(Binder.getCallingUid(), installerPackageName);
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final AndroidPackage callingInstaller = pmi.getPackage(initiatingPackageName);
+ final AndroidPackage callingInstaller = pmi.getPackage(installerPackageName);
if (callingInstaller == null) {
throw new IllegalStateException("Can't obtain calling installer's package.");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b1b05bedfcad..4c7243decb07 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1254,9 +1254,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (applicationInfo == null) {
throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
}
+
final InstallSourceInfo installSourceInfo = snapshot.getInstallSourceInfo(packageName);
- final String installerPackageName =
- installSourceInfo != null ? installSourceInfo.getInitiatingPackageName() : null;
+ final String installerPackageName;
+ if (installSourceInfo != null) {
+ if (!TextUtils.isEmpty(installSourceInfo.getInitiatingPackageName())) {
+ installerPackageName = installSourceInfo.getInitiatingPackageName();
+ } else {
+ installerPackageName = installSourceInfo.getInstallingPackageName();
+ }
+ } else {
+ installerPackageName = null;
+ }
List<Pair<String, File>> filesToChecksum = new ArrayList<>();
@@ -1891,6 +1900,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
/* excludePreCreated= */ false));
t.traceEnd();
+ if (mFirstBoot) {
+ t.traceBegin("setFirstBoot: ");
+ try {
+ mInstaller.setFirstBoot();
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Could not set First Boot: ", e);
+ }
+ t.traceEnd();
+ }
+
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
mPermissionManager.readLegacyPermissionStateTEMP();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c8f809b6782f..7a73412c9b9f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -19,9 +19,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Person;
+import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.BatchResultCallback;
+import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByDocumentIdRequest;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
@@ -2384,14 +2387,24 @@ class ShortcutPackage extends ShortcutPackageItem {
}
runAsSystem(() -> fromAppSearch().thenAccept(session -> {
session.getByDocumentId(new GetByDocumentIdRequest.Builder(getPackageName())
- .addIds(ids).build(), mShortcutUser.mExecutor, result -> {
- final List<ShortcutInfo> ret = result.getSuccesses().values()
- .stream().map(doc ->
- ShortcutInfo.createFromGenericDocument(
- mShortcutUser.getUserId(), doc))
- .collect(Collectors.toList());
- cb.accept(ret);
- });
+ .addIds(ids).build(), mShortcutUser.mExecutor,
+ new BatchResultCallback<String, GenericDocument>() {
+ @Override
+ public void onResult(
+ @NonNull AppSearchBatchResult<String, GenericDocument> result) {
+ final List<ShortcutInfo> ret = result.getSuccesses().values()
+ .stream().map(doc ->
+ ShortcutInfo.createFromGenericDocument(
+ mShortcutUser.getUserId(), doc))
+ .collect(Collectors.toList());
+ cb.accept(ret);
+ }
+ @Override
+ public void onSystemError(
+ @Nullable Throwable throwable) {
+ Slog.d(TAG, "Error retrieving shortcuts", throwable);
+ }
+ });
}));
}
@@ -2407,15 +2420,24 @@ class ShortcutPackage extends ShortcutPackageItem {
runAsSystem(() -> fromAppSearch().thenAccept(session ->
session.remove(
new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(ids).build(),
- mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- final Map<String, AppSearchResult<Void>> failures =
- result.getFailures();
- for (String key : failures.keySet()) {
- Slog.e(TAG, "Failed deleting " + key + ", error message:"
- + failures.get(key).getErrorMessage());
+ mShortcutUser.mExecutor,
+ new BatchResultCallback<String, Void>() {
+ @Override
+ public void onResult(
+ @NonNull AppSearchBatchResult<String, Void> result) {
+ if (!result.isSuccess()) {
+ final Map<String, AppSearchResult<Void>> failures =
+ result.getFailures();
+ for (String key : failures.keySet()) {
+ Slog.e(TAG, "Failed deleting " + key + ", error message:"
+ + failures.get(key).getErrorMessage());
+ }
}
}
+ @Override
+ public void onSystemError(@Nullable Throwable throwable) {
+ Slog.e(TAG, "Error removing shortcuts", throwable);
+ }
})));
}
@@ -2452,12 +2474,20 @@ class ShortcutPackage extends ShortcutPackageItem {
AppSearchShortcutInfo.toGenericDocuments(shortcuts))
.build(),
mShortcutUser.mExecutor,
- result -> {
- if (!result.isSuccess()) {
- for (AppSearchResult<Void> k : result.getFailures().values()) {
- Slog.e(TAG, k.getErrorMessage());
+ new BatchResultCallback<String, Void>() {
+ @Override
+ public void onResult(
+ @NonNull AppSearchBatchResult<String, Void> result) {
+ if (!result.isSuccess()) {
+ for (AppSearchResult<Void> k : result.getFailures().values()) {
+ Slog.e(TAG, k.getErrorMessage());
+ }
}
}
+ @Override
+ public void onSystemError(@Nullable Throwable throwable) {
+ Slog.d(TAG, "Error persisting shortcuts", throwable);
+ }
});
}));
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 70053bdeb47e..d340561c2862 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1387,7 +1387,7 @@ public class UserManagerService extends IUserManager.Stub {
*/
@Override
public boolean isUserOfType(@UserIdInt int userId, String userType) {
- checkManageUsersPermission("check user type");
+ checkQueryOrCreateUsersPermission("check user type");
return userType != null && userType.equals(getUserTypeNoChecks(userId));
}
@@ -1643,7 +1643,7 @@ public class UserManagerService extends IUserManager.Stub {
if (!hasQueryOrCreateUsersPermission()
&& !hasPermissionGranted(
android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
- throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
+ throw new SecurityException("You need MANAGE_USERS, CREATE_USERS, QUERY_USERS, or "
+ "GET_ACCOUNTS_PRIVILEGED permissions to: get user name");
}
final int userId = UserHandle.getUserId(callingUid);
@@ -5064,9 +5064,13 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isUserNameSet(@UserIdInt int userId) {
- if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
- throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED "
- + "permissions to: get whether user name is set");
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (!hasQueryOrCreateUsersPermission()
+ && !(callingUserId == userId && hasPermissionGranted(
+ android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid))) {
+ throw new SecurityException("You need MANAGE_USERS, CREATE_USERS, QUERY_USERS, or "
+ + "GET_ACCOUNTS_PRIVILEGED permissions to: get whether user name is set");
}
synchronized (mUsersLock) {
final UserInfo userInfo = getUserInfoLU(userId);
@@ -5424,32 +5428,62 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ARG_CRITICAL_ONLY = "--critical-only";
private static final String ARG_MODE = "--mode";
- private int onShellCommand(Shell shell, String cmd) {
+ private final class Shell extends ShellCommand {
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.printf("User manager (user) commands:\n");
+
+ pw.printf("%s%s\n", PREFIX_HELP_COMMAND, CMD_HELP);
+ pw.printf("%sPrints this help text.\n\n", PREFIX_HELP_DESCRIPTION);
+
+ pw.printf("%s%s [%s] [%s]\n", PREFIX_HELP_COMMAND, CMD_LIST, ARG_V, ARG_ALL);
+ pw.printf("%sPrints all users on the system.\n\n", PREFIX_HELP_DESCRIPTION);
+
+ pw.printf("%s%s [%s | %s] [%s] [%s MODE]\n", PREFIX_HELP_COMMAND,
+ CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS,
+ ARG_V, ARG_VERBOSE, ARG_CRITICAL_ONLY, ARG_MODE);
+
+ pw.printf("%sReports all issues on user-type package allowlist XML files. Options:\n",
+ PREFIX_HELP_DESCRIPTION);
+ pw.printf("%s%s | %s: shows extra info, like number of issues\n",
+ PREFIX_HELP_DESCRIPTION, ARG_V, ARG_VERBOSE);
+ pw.printf("%s%s: show only critical issues, excluding warnings\n",
+ PREFIX_HELP_DESCRIPTION, ARG_CRITICAL_ONLY);
+ pw.printf("%s%s MODE: shows what errors would be if device used mode MODE\n"
+ + "%s(where MODE is the allowlist mode integer as defined by "
+ + "config_userTypePackageWhitelistMode)\n\n",
+ PREFIX_HELP_DESCRIPTION, ARG_MODE, PREFIX_HELP_DESCRIPTION_EXTRA_LINES);
+ }
+
+ @Override
+ public int onCommand(String cmd) {
if (cmd == null) {
- return shell.handleDefaultCommands(cmd);
+ return handleDefaultCommands(cmd);
}
- final PrintWriter pw = shell.getOutPrintWriter();
try {
switch(cmd) {
case CMD_LIST:
- return runList(pw, shell);
+ return runList();
case CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS:
- return runReportPackageWhitelistProblems(pw, shell);
+ return runReportPackageAllowlistProblems();
default:
- return shell.handleDefaultCommands(cmd);
+ return handleDefaultCommands(cmd);
}
} catch (RemoteException e) {
- pw.println("Remote exception: " + e);
+ getOutPrintWriter().println("Remote exception: " + e);
}
return -1;
}
- private int runList(PrintWriter pw, Shell shell) throws RemoteException {
+ private int runList() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
boolean all = false;
boolean verbose = false;
String opt;
- while ((opt = shell.getNextOption()) != null) {
+ while ((opt = getNextOption()) != null) {
switch (opt) {
case ARG_V:
verbose = true;
@@ -5528,12 +5562,13 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private int runReportPackageWhitelistProblems(PrintWriter pw, Shell shell) {
+ private int runReportPackageAllowlistProblems() {
+ final PrintWriter pw = getOutPrintWriter();
boolean verbose = false;
boolean criticalOnly = false;
int mode = UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_NONE;
String opt;
- while ((opt = shell.getNextOption()) != null) {
+ while ((opt = getNextOption()) != null) {
switch (opt) {
case ARG_V:
case ARG_VERBOSE:
@@ -5543,7 +5578,7 @@ public class UserManagerService extends IUserManager.Stub {
criticalOnly = true;
break;
case ARG_MODE:
- mode = Integer.parseInt(shell.getNextArgRequired());
+ mode = Integer.parseInt(getNextArgRequired());
break;
default:
pw.println("Invalid option: " + opt);
@@ -5551,15 +5586,17 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- Slog.d(LOG_TAG, "runReportPackageWhitelistProblems(): verbose=" + verbose
+ Slog.d(LOG_TAG, "runReportPackageAllowlistProblems(): verbose=" + verbose
+ ", criticalOnly=" + criticalOnly
+ ", mode=" + UserSystemPackageInstaller.modeToString(mode));
try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
- mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose, criticalOnly);
+ mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose,
+ criticalOnly);
}
return 0;
}
+ }
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -6254,40 +6291,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private class Shell extends ShellCommand {
- @Override
- public int onCommand(String cmd) {
- return onShellCommand(this, cmd);
- }
-
- @Override
- public void onHelp() {
- final PrintWriter pw = getOutPrintWriter();
- pw.printf("User manager (user) commands:\n");
-
- pw.printf("%s%s\n", PREFIX_HELP_COMMAND, CMD_HELP);
- pw.printf("%sPrints this help text.\n\n", PREFIX_HELP_DESCRIPTION);
-
- pw.printf("%s%s [%s] [%s]\n", PREFIX_HELP_COMMAND, CMD_LIST, ARG_V, ARG_ALL);
- pw.printf("%sPrints all users on the system.\n\n", PREFIX_HELP_DESCRIPTION);
-
- pw.printf("%s%s [%s | %s] [%s] [%s MODE]\n", PREFIX_HELP_COMMAND,
- CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS,
- ARG_V, ARG_VERBOSE, ARG_CRITICAL_ONLY, ARG_MODE);
-
- pw.printf("%sReports all issues on user-type package allowlist XML files. Options:\n",
- PREFIX_HELP_DESCRIPTION);
- pw.printf("%s%s | %s: shows extra info, like number of issues\n",
- PREFIX_HELP_DESCRIPTION, ARG_V, ARG_VERBOSE);
- pw.printf("%s%s: show only critical issues, excluding warnings\n",
- PREFIX_HELP_DESCRIPTION, ARG_CRITICAL_ONLY);
- pw.printf("%s%s MODE: shows what errors would be if device used mode MODE\n"
- + "%s(where MODE is the allowlist mode integer as defined by "
- + "config_userTypePackageWhitelistMode)\n\n",
- PREFIX_HELP_DESCRIPTION, ARG_MODE, PREFIX_HELP_DESCRIPTION_EXTRA_LINES);
- }
- }
-
private static void debug(String message) {
Slog.d(LOG_TAG, message
+ (DBG_WITH_STACKTRACE ? " called at\n" + Debug.getCallers(10, " ") : ""));
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 60602337ba1a..881f8707fdd8 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -317,9 +317,21 @@ public class OneTimePermissionUserManager {
synchronized (mInnerLock) {
mIsFinished = true;
cancelAlarmLocked();
- mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
- mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
- mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ try {
+ mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Could not remove start timer listener", e);
+ }
+ try {
+ mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Could not remove session killable listener", e);
+ }
+ try {
+ mActivityManager.removeOnUidImportanceListener(mGoneListener);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Could not remove gone listener", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 70ef3d364277..bd9e8923e984 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -153,9 +153,10 @@ public final class PermissionPolicyService extends SystemService {
private List<String> mAppOpPermissions;
- private Context mContext;
- private Handler mHandler;
+ private final Context mContext;
+ private final Handler mHandler;
private PackageManagerInternal mPackageManagerInternal;
+ private PermissionManagerServiceInternal mPermissionManagerInternal;
private NotificationManagerInternal mNotificationManager;
private final KeyguardManager mKeyguardManager;
private final PackageManager mPackageManager;
@@ -174,7 +175,7 @@ public final class PermissionPolicyService extends SystemService {
public void onStart() {
mPackageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
- PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
+ mPermissionManagerInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -206,7 +207,7 @@ public final class PermissionPolicyService extends SystemService {
}
});
- permissionManagerInternal.addOnRuntimePermissionStateChangedListener(
+ mPermissionManagerInternal.addOnRuntimePermissionStateChangedListener(
this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
mAppOpsCallback = new IAppOpsCallback.Stub() {
@@ -218,7 +219,7 @@ public final class PermissionPolicyService extends SystemService {
};
final ArrayList<PermissionInfo> dangerousPerms =
- permissionManagerInternal.getAllPermissionsWithProtection(
+ mPermissionManagerInternal.getAllPermissionsWithProtection(
PermissionInfo.PROTECTION_DANGEROUS);
try {
int numDangerousPerms = dangerousPerms.size();
@@ -243,7 +244,7 @@ public final class PermissionPolicyService extends SystemService {
}
final List<PermissionInfo> appOpPermissionInfos =
- permissionManagerInternal.getAllPermissionsWithProtectionFlags(
+ mPermissionManagerInternal.getAllPermissionsWithProtectionFlags(
PermissionInfo.PROTECTION_FLAG_APPOP);
mAppOpPermissions = new ArrayList<>();
final int appOpPermissionInfosSize = appOpPermissionInfos.size();
@@ -1283,10 +1284,12 @@ public final class PermissionPolicyService extends SystemService {
}
boolean hasCreatedNotificationChannels = mNotificationManager
.getNumNotificationChannelsForPackage(pkgName, uid, true) > 0;
+ boolean granted = mPermissionManagerInternal.checkUidPermission(uid, POST_NOTIFICATIONS)
+ == PackageManager.PERMISSION_GRANTED;
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
- return hasCreatedNotificationChannels && (needsReview || !explicitlySet);
+ return !granted && hasCreatedNotificationChannels && (needsReview || !explicitlySet);
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1ac373f074ec..5aa81ac9086d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5446,8 +5446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
VibrationAttributes attrs = getVibrationAttributes(effectId);
if (always) {
attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
- VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build();
}
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index adca21676f9d..d3748140a5a5 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -40,12 +40,16 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.service.trust.GrantTrustResult;
import android.service.trust.ITrustAgentService;
import android.service.trust.ITrustAgentServiceCallback;
import android.service.trust.TrustAgentService;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.Collections;
import java.util.List;
@@ -156,7 +160,9 @@ public class TrustAgentWrapper {
}
mTrusted = true;
mTrustable = false;
- mMessage = (CharSequence) msg.obj;
+ Pair<CharSequence, AndroidFuture<GrantTrustResult>> pair = (Pair) msg.obj;
+ mMessage = pair.first;
+ AndroidFuture<GrantTrustResult> resultCallback = pair.second;
int flags = msg.arg1;
mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
@@ -189,7 +195,7 @@ public class TrustAgentWrapper {
mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
(mMessage != null ? mMessage.toString() : null),
durationMs, flags);
- mTrustManagerService.updateTrust(mUserId, flags);
+ mTrustManagerService.updateTrust(mUserId, flags, resultCallback);
break;
case MSG_TRUST_TIMEOUT:
if (DEBUG) Slog.d(TAG, "Trust timed out : " + mName.flattenToShortString());
@@ -314,13 +320,18 @@ public class TrustAgentWrapper {
private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
@Override
- public void grantTrust(CharSequence message, long durationMs, int flags) {
+ public void grantTrust(
+ CharSequence message,
+ long durationMs,
+ int flags,
+ AndroidFuture resultCallback) {
if (DEBUG) {
Slog.d(TAG, "enableTrust(" + message + ", durationMs = " + durationMs
+ ", flags = " + flags + ")");
}
- Message msg = mHandler.obtainMessage(MSG_GRANT_TRUST, flags, 0, message);
+ Message msg = mHandler.obtainMessage(
+ MSG_GRANT_TRUST, flags, 0, Pair.create(message, resultCallback));
msg.getData().putLong(DATA_DURATION, durationMs);
msg.sendToTarget();
}
@@ -514,12 +525,23 @@ public class TrustAgentWrapper {
}
/**
- * @see android.service.trust.TrustAgentService#onUserRequestedUnlock()
+ * @see android.service.trust.TrustAgentService#onUserRequestedUnlock(boolean)
*/
- public void onUserRequestedUnlock() {
+ public void onUserRequestedUnlock(boolean dismissKeyguard) {
+ try {
+ if (mTrustAgentService != null) {
+ mTrustAgentService.onUserRequestedUnlock(dismissKeyguard);
+ }
+ } catch (RemoteException e) {
+ onError(e);
+ }
+ }
+
+ /** @see android.service.trust.TrustAgentService#onUserMayRequestUnlock() */
+ public void onUserMayRequestUnlock() {
try {
if (mTrustAgentService != null) {
- mTrustAgentService.onUserRequestedUnlock();
+ mTrustAgentService.onUserMayRequestUnlock();
}
} catch (RemoteException e) {
onError(e);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index cc1d0e2ea6c8..a486364518b9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -61,6 +61,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.security.Authorization;
+import android.service.trust.GrantTrustResult;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -77,6 +78,7 @@ import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
@@ -131,6 +133,7 @@ public class TrustManagerService extends SystemService {
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
private static final int MSG_USER_REQUESTED_UNLOCK = 16;
private static final int MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH = 17;
+ private static final int MSG_USER_MAY_REQUEST_UNLOCK = 18;
private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
@@ -495,13 +498,28 @@ public class TrustManagerService extends SystemService {
}
}
- public void updateTrust(int userId, int flags) {
- updateTrust(userId, flags, false /* isFromUnlock */);
+ /** Triggers a trust update. */
+ public void updateTrust(
+ int userId,
+ int flags) {
+ updateTrust(userId, flags, null);
}
- private void updateTrust(int userId, int flags, boolean isFromUnlock) {
+ /** Triggers a trust update. */
+ public void updateTrust(
+ int userId,
+ int flags,
+ @Nullable AndroidFuture<GrantTrustResult> resultCallback) {
+ updateTrust(userId, flags, false /* isFromUnlock */, resultCallback);
+ }
+
+ private void updateTrust(
+ int userId,
+ int flags,
+ boolean isFromUnlock,
+ @Nullable AndroidFuture<GrantTrustResult> resultCallback) {
if (ENABLE_ACTIVE_UNLOCK_FLAG) {
- updateTrustWithRenewableUnlock(userId, flags, isFromUnlock);
+ updateTrustWithRenewableUnlock(userId, flags, isFromUnlock, resultCallback);
} else {
updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock);
}
@@ -553,7 +571,11 @@ public class TrustManagerService extends SystemService {
}
}
- private void updateTrustWithRenewableUnlock(int userId, int flags, boolean isFromUnlock) {
+ private void updateTrustWithRenewableUnlock(
+ int userId,
+ int flags,
+ boolean isFromUnlock,
+ @Nullable AndroidFuture<GrantTrustResult> resultCallback) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -614,8 +636,18 @@ public class TrustManagerService extends SystemService {
isTrustableTimeout /* isTrustableTimeout */);
}
}
- }
+ boolean wasLocked = !alreadyUnlocked;
+ boolean shouldSendCallback = wasLocked && pendingTrustState == TrustState.TRUSTED;
+ if (shouldSendCallback) {
+ if (resultCallback != null) {
+ if (DEBUG) Slog.d(TAG, "calling back with UNLOCKED_BY_GRANT");
+ resultCallback.complete(
+ GrantTrustResult.withStatus(
+ GrantTrustResult.STATUS_UNLOCKED_BY_GRANT));
+ }
+ }
+ }
private void updateTrustUsuallyManaged(int userId, boolean managed) {
synchronized (mTrustUsuallyManagedForUser) {
@@ -1190,7 +1222,7 @@ public class TrustManagerService extends SystemService {
if (successful) {
mStrongAuthTracker.allowTrustFromUnlock(userId);
// Allow the presence of trust on a successful unlock attempt to extend unlock
- updateTrust(userId, 0 /* flags */, true);
+ updateTrust(userId, 0 /* flags */, true, null);
mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget();
}
@@ -1202,11 +1234,27 @@ public class TrustManagerService extends SystemService {
}
}
- private void dispatchUserRequestedUnlock(int userId) {
+ private void dispatchUserRequestedUnlock(int userId, boolean dismissKeyguard) {
+ if (DEBUG) {
+ Slog.d(TAG, "dispatchUserRequestedUnlock(user=" + userId + ", dismissKeyguard="
+ + dismissKeyguard + ")");
+ }
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
- info.agent.onUserRequestedUnlock();
+ info.agent.onUserRequestedUnlock(dismissKeyguard);
+ }
+ }
+ }
+
+ private void dispatchUserMayRequestUnlock(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "dispatchUserMayRequestUnlock(user=" + userId + ")");
+ }
+ for (int i = 0; i < mActiveAgents.size(); i++) {
+ AgentInfo info = mActiveAgents.valueAt(i);
+ if (info.userId == userId) {
+ info.agent.onUserMayRequestUnlock();
}
}
}
@@ -1341,9 +1389,17 @@ public class TrustManagerService extends SystemService {
}
@Override
- public void reportUserRequestedUnlock(int userId) throws RemoteException {
+ public void reportUserRequestedUnlock(int userId, boolean dismissKeyguard)
+ throws RemoteException {
enforceReportPermission();
- mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId).sendToTarget();
+ mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId, dismissKeyguard ? 1 : 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void reportUserMayRequestUnlock(int userId) throws RemoteException {
+ enforceReportPermission();
+ mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK, userId).sendToTarget();
}
@Override
@@ -1683,7 +1739,10 @@ public class TrustManagerService extends SystemService {
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
case MSG_USER_REQUESTED_UNLOCK:
- dispatchUserRequestedUnlock(msg.arg1);
+ dispatchUserRequestedUnlock(msg.arg1, msg.arg2 != 0);
+ break;
+ case MSG_USER_MAY_REQUEST_UNLOCK:
+ dispatchUserMayRequestUnlock(msg.arg1);
break;
case MSG_DISPATCH_UNLOCK_LOCKOUT:
dispatchUnlockLockout(msg.arg1, msg.arg2);
@@ -1725,7 +1784,7 @@ public class TrustManagerService extends SystemService {
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
if (msg.arg2 == 1) {
- updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */);
+ updateTrust(msg.arg1, 0 /* flags */, true /* isFromUnlock */, null);
}
final int unlockedUser = msg.getData().getInt(
REFRESH_DEVICE_LOCKED_EXCEPT_USER, UserHandle.USER_NULL);
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index f02f9f98d933..8ecc51b3087c 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -47,12 +47,16 @@ final class Vibration {
FINISHED,
FINISHED_UNEXPECTED, // Didn't terminate in the usual way.
FORWARDED_TO_INPUT_DEVICES,
- CANCELLED,
+ CANCELLED_BINDER_DIED,
+ CANCELLED_BY_SCREEN_OFF,
+ CANCELLED_BY_SETTINGS_UPDATE,
+ CANCELLED_BY_USER,
+ CANCELLED_BY_UNKNOWN_REASON,
+ CANCELLED_SUPERSEDED,
IGNORED_ERROR_APP_OPS,
IGNORED_ERROR_CANCELLING,
IGNORED_ERROR_SCHEDULING,
IGNORED_ERROR_TOKEN,
- IGNORED,
IGNORED_APP_OPS,
IGNORED_BACKGROUND,
IGNORED_UNKNOWN_VIBRATION,
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 77da75118958..e9535e0a56e1 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -121,6 +121,11 @@ final class VibrationSettings {
USAGE_PHYSICAL_EMULATION,
USAGE_HARDWARE_FEEDBACK));
+ private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
+ new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
+ new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -130,11 +135,11 @@ final class VibrationSettings {
private final Object mLock = new Object();
private final Context mContext;
private final String mSystemUiPackage;
- private final SettingsObserver mSettingObserver;
+ private final SettingsContentObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
@VisibleForTesting
- final UserObserver mUserReceiver;
+ final SettingsBroadcastReceiver mSettingChangeReceiver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -154,6 +159,8 @@ final class VibrationSettings {
private boolean mBatterySaverMode;
@GuardedBy("mLock")
private boolean mVibrateOn;
+ @GuardedBy("mLock")
+ private int mRingerMode;
VibrationSettings(Context context, Handler handler) {
this(context, handler, new VibrationConfig(context.getResources()));
@@ -163,9 +170,9 @@ final class VibrationSettings {
VibrationSettings(Context context, Handler handler, VibrationConfig config) {
mContext = context;
mVibrationConfig = config;
- mSettingObserver = new SettingsObserver(handler);
+ mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new UidObserver();
- mUserReceiver = new UserObserver();
+ mSettingChangeReceiver = new SettingsBroadcastReceiver();
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -188,12 +195,13 @@ final class VibrationSettings {
VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));
// Update with current values from settings.
- updateSettings();
+ update();
}
public void onSystemReady() {
synchronized (mLock) {
mAudioManager = mContext.getSystemService(AudioManager.class);
+ mRingerMode = mAudioManager.getRingerModeInternal();
}
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
@@ -224,8 +232,8 @@ final class VibrationSettings {
}
});
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(mUserReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
+ registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
+ registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
// Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
@@ -248,7 +256,7 @@ final class VibrationSettings {
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
// Update with newly loaded services.
- updateSettings();
+ update();
}
/**
@@ -396,16 +404,17 @@ final class VibrationSettings {
// Only ringtone and notification vibrations are disabled when phone is on silent mode.
return true;
}
- // If audio manager was not loaded yet then assume most restrictive mode.
- int ringerMode = (mAudioManager == null)
- ? AudioManager.RINGER_MODE_SILENT
- : mAudioManager.getRingerModeInternal();
- return ringerMode != AudioManager.RINGER_MODE_SILENT;
+ return mRingerMode != AudioManager.RINGER_MODE_SILENT;
}
- /** Updates all vibration settings and triggers registered listeners. */
- @VisibleForTesting
- void updateSettings() {
+ /** Update all cached settings and triggers registered listeners. */
+ void update() {
+ updateSettings();
+ updateRingerMode();
+ notifyListeners();
+ }
+
+ private void updateSettings() {
synchronized (mLock) {
mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
@@ -435,7 +444,6 @@ final class VibrationSettings {
loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
getDefaultIntensity(USAGE_RINGTONE));
-
mCurrentVibrationIntensities.clear();
mCurrentVibrationIntensities.put(USAGE_ALARM, alarmIntensity);
mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity);
@@ -469,7 +477,16 @@ final class VibrationSettings {
// A11y is not disabled by any haptic feedback setting.
mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity);
}
- notifyListeners();
+ }
+
+ private void updateRingerMode() {
+ synchronized (mLock) {
+ // If audio manager was not loaded yet then assume most restrictive mode.
+ // This will be loaded again as soon as the audio manager is loaded in onSystemReady.
+ mRingerMode = (mAudioManager == null)
+ ? AudioManager.RINGER_MODE_SILENT
+ : mAudioManager.getRingerModeInternal();
+ }
}
@Override
@@ -586,6 +603,11 @@ final class VibrationSettings {
UserHandle.USER_ALL);
}
+ private void registerSettingsChangeReceiver(IntentFilter intentFilter) {
+ mContext.registerReceiver(mSettingChangeReceiver, intentFilter,
+ Context.RECEIVER_NOT_EXPORTED);
+ }
+
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
long[] timings = getLongIntArray(mContext.getResources(), resId);
@@ -616,24 +638,33 @@ final class VibrationSettings {
}
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
- private final class SettingsObserver extends ContentObserver {
- SettingsObserver(Handler handler) {
+ private final class SettingsContentObserver extends ContentObserver {
+ SettingsContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
updateSettings();
+ notifyListeners();
}
}
- /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */
+ /**
+ * Implementation of {@link BroadcastReceiver} to update settings on current user or ringer
+ * mode change.
+ */
@VisibleForTesting
- final class UserObserver extends BroadcastReceiver {
+ final class SettingsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- updateSettings();
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ // Reload all settings, as they are user-based.
+ update();
+ } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+ updateRingerMode();
+ notifyListeners();
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index e12426b2b02c..e3d806755c6e 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -79,12 +79,14 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final IntArray mSignalVibratorsComplete;
+ @Nullable
@GuardedBy("mLock")
- private boolean mSignalCancel = false;
+ private Vibration.Status mSignalCancelStatus = null;
@GuardedBy("mLock")
private boolean mSignalCancelImmediate = false;
- private boolean mCancelled = false;
+ @Nullable
+ private Vibration.Status mCancelStatus = null;
private boolean mCancelledImmediately = false; // hard stop
private int mPendingVibrateSteps;
private int mRemainingStartSequentialEffectSteps;
@@ -185,8 +187,8 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
expectIsVibrationThread(true);
}
- if (mCancelled) {
- return Vibration.Status.CANCELLED;
+ if (mCancelStatus != null) {
+ return mCancelStatus;
}
if (mPendingVibrateSteps > 0
|| mRemainingStartSequentialEffectSteps > 0) {
@@ -303,7 +305,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- notifyCancelled(/* immediate= */ false);
+ notifyCancelled(Vibration.Status.CANCELLED_BINDER_DIED, /* immediate= */ false);
}
/**
@@ -312,22 +314,41 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
*
* @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
*/
- public void notifyCancelled(boolean immediate) {
+ public void notifyCancelled(@NonNull Vibration.Status cancelStatus, boolean immediate) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel requested with status=" + cancelStatus
+ + ", immediate=" + immediate);
+ }
+ if ((cancelStatus == null) || !cancelStatus.name().startsWith("CANCEL")) {
+ Slog.w(TAG, "Vibration cancel requested with bad status=" + cancelStatus
+ + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
+ cancelStatus = Vibration.Status.CANCELLED_BY_UNKNOWN_REASON;
+ }
synchronized (mLock) {
- if (immediate && mSignalCancelImmediate || mSignalCancel) {
- // Nothing to update: already cancelled previously.
+ if (immediate && mSignalCancelImmediate || (mSignalCancelStatus != null)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel request ignored as the vibration "
+ + mVibration.id + "is already being cancelled with status="
+ + mSignalCancelStatus + ", immediate=" + mSignalCancelImmediate);
+ }
return;
}
mSignalCancelImmediate |= immediate;
- mSignalCancel = true;
+ if (mSignalCancelStatus == null) {
+ mSignalCancelStatus = cancelStatus;
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel request new status=" + cancelStatus
+ + " ignored as the vibration was already cancelled with status="
+ + mSignalCancelStatus + ", but immediate flag was updated to "
+ + mSignalCancelImmediate);
+ }
+ }
mLock.notify();
}
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancel requested, immediate=" + immediate);
- }
}
/**
@@ -380,7 +401,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals.
}
- return (mSignalCancel && !mCancelled)
+ return (mSignalCancelStatus != mCancelStatus)
|| (mSignalCancelImmediate && !mCancelledImmediately)
|| (mSignalVibratorsComplete.size() > 0);
}
@@ -395,7 +416,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
}
int[] vibratorsToProcess = null;
- boolean doCancel = false;
+ Vibration.Status doCancelStatus = null;
boolean doCancelImmediate = false;
// Collect signals to process, but don't keep the lock while processing them.
synchronized (mLock) {
@@ -405,9 +426,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
}
// This should only happen once.
doCancelImmediate = true;
+ doCancelStatus = mSignalCancelStatus;
}
- if (mSignalCancel && !mCancelled) {
- doCancel = true;
+ if (mSignalCancelStatus != mCancelStatus) {
+ doCancelStatus = mSignalCancelStatus;
}
if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
// Swap out the queue of completions to process.
@@ -421,11 +443,11 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
// completion signals that were collected in this call, but we won't process them
// anyway as all steps are cancelled.
if (doCancelImmediate) {
- processCancelImmediately();
+ processCancelImmediately(doCancelStatus);
return;
}
- if (doCancel) {
- processCancel();
+ if (doCancelStatus != null) {
+ processCancel(doCancelStatus);
}
if (vibratorsToProcess != null) {
processVibratorsComplete(vibratorsToProcess);
@@ -438,12 +460,12 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
* <p>This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void processCancel() {
+ public void processCancel(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- mCancelled = true;
+ mCancelStatus = cancelStatus;
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
// and clean up steps are added back to the queue, so they can handle the callback.
List<Step> cleanUpSteps = new ArrayList<>();
@@ -461,13 +483,13 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
*
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void processCancelImmediately() {
+ public void processCancelImmediately(Vibration.Status cancelStatus) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
mCancelledImmediately = true;
- mCancelled = true;
+ mCancelStatus = cancelStatus;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 1260e5dbc9f7..f749773d14a0 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -162,10 +162,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
if (shouldCancelOnScreenOffLocked(mNextVibration)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_SCREEN_OFF);
}
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SCREEN_OFF,
+ /* immediate= */ false);
}
}
}
@@ -401,6 +402,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
uid, opPkg, reason);
fillVibrationFallbacks(vib, effect);
+ if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
+ // Force update of user settings before checking if this vibration effect should
+ // be ignored or scaled.
+ mVibrationSettings.update();
+ }
+
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
@@ -420,7 +427,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final long ident = Binder.clearCallingIdentity();
try {
if (mCurrentVibration != null) {
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ false);
}
Vibration.Status status = startVibrationLocked(vib);
if (status != Vibration.Status.RUNNING) {
@@ -453,19 +461,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
- clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ clearNextVibrationLocked(Vibration.Status.CANCELLED_BY_USER);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
usageFilter, token)) {
- mCurrentVibration.notifyCancelled(/* immediate= */false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_USER,
+ /* immediate= */false);
}
if (mCurrentExternalVibration != null
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BY_USER,
/* continueExternalControl= */ false);
}
} finally {
@@ -594,7 +603,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
- mCurrentVibration.notifyCancelled(/* immediate= */ false);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE,
+ /* immediate= */ false);
}
}
}
@@ -1313,7 +1323,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.d(TAG, "External vibration finished because binder died");
}
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_BINDER_DIED,
/* continueExternalControl= */ false);
}
}
@@ -1506,12 +1516,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return IExternalVibratorService.SCALE_MUTE;
}
+ VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(),
+ /* effect= */ null);
+ if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
+ // Force update of user settings before checking if this vibration effect should
+ // be ignored or scaled.
+ mVibrationSettings.update();
+ }
+
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
int scale;
synchronized (mLock) {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
- vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
+ vib.getUid(), vib.getPackage(), attrs);
if (ignoreStatus != null) {
ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
@@ -1529,7 +1547,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
- mCurrentVibration.notifyCancelled(/* immediate= */ true);
+ mCurrentVibration.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED,
+ /* immediate= */ true);
waitForCompletion = true;
}
} else {
@@ -1543,13 +1562,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// would need to mute the old one still if it came from a different controller.
alreadyUnderExternalControl = true;
mCurrentExternalVibration.externalVibration.mute();
- endExternalVibrateLocked(Vibration.Status.CANCELLED,
+ endExternalVibrateLocked(Vibration.Status.CANCELLED_SUPERSEDED,
/* continueExternalControl= */ true);
}
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
vib.linkToDeath(mCurrentExternalVibration);
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
- vib.getVibrationAttributes().getUsage());
+ attrs.getUsage());
scale = mCurrentExternalVibration.scale;
}
@@ -1908,7 +1927,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
final int flags =
commonOptions.force ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY : 0;
return new VibrationAttributes.Builder()
- .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+ .setFlags(flags)
// Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects.
.setUsage(VibrationAttributes.USAGE_TOUCH)
.build();
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 11ddac6a9740..799d59c617ea 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -811,8 +811,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
RectF windowFrame = TEMP_RECTF;
windowFrame.set(rect);
- inverseMatrix.mapRect(windowFrame);
displayMatrix.mapRect(windowFrame);
+ inverseMatrix.mapRect(windowFrame);
// Union all rects.
outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
(int) windowFrame.right, (int) windowFrame.bottom));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4bef126ee2c8..2c9372e7eaca 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -116,6 +116,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -216,7 +217,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
@@ -7237,11 +7237,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getDisplayContent().computeImeTargetIfNeeded(this);
- if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this
- + ": reportedVisible=" + reportedVisible
- + " okToDisplay=" + okToDisplay()
- + " okToAnimate=" + okToAnimate()
- + " startingDisplayed=" + startingDisplayed);
+ ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s"
+ + ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
+ this, reportedVisible, okToDisplay(), okToAnimate(), startingDisplayed);
// clean up thumbnail window
if (mThumbnail != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 8622bd32fc68..ce49a8675890 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -18,20 +18,11 @@ package com.android.server.wm;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
-import android.os.IBinder;
import android.os.InputConfig;
-import android.os.InputConstants;
-import android.os.Looper;
import android.os.Process;
-import android.util.Slog;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
-import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.widget.Toast;
/**
* Creates a InputWindowHandle that catches all touches that would otherwise pass through an
@@ -45,20 +36,12 @@ class ActivityRecordInputSink {
@ChangeId
static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
- private static final String TAG = "ActivityRecordInputSink";
- private static final int NUMBER_OF_TOUCHES_TO_DISABLE = 3;
- private static final long TOAST_COOL_DOWN_MILLIS = 3000L;
-
private final ActivityRecord mActivityRecord;
private final boolean mIsCompatEnabled;
private final String mName;
- // Hold on to InputEventReceiver to prevent it from getting GCd.
- private InputEventReceiver mInputEventReceiver;
private InputWindowHandleWrapper mInputWindowHandleWrapper;
private SurfaceControl mSurfaceControl;
- private int mRapidTouchCount = 0;
- private IBinder mToken;
private boolean mDisabled = false;
ActivityRecordInputSink(ActivityRecord activityRecord) {
@@ -66,7 +49,7 @@ class ActivityRecordInputSink {
mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES,
mActivityRecord.getUid());
mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink "
- + mActivityRecord.mActivityComponent.getShortClassName();
+ + mActivityRecord.mActivityComponent.flattenToShortString();
}
public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) {
@@ -93,16 +76,13 @@ class ActivityRecordInputSink {
private InputWindowHandleWrapper getInputWindowHandleWrapper() {
if (mInputWindowHandleWrapper == null) {
mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle());
- InputChannel inputChannel =
- mActivityRecord.mWmService.mInputManager.createInputChannel(mName);
- mToken = inputChannel.getToken();
- mInputEventReceiver = createInputEventReceiver(inputChannel);
}
if (mDisabled || !mIsCompatEnabled || mActivityRecord.isInTransition()) {
// TODO(b/208662670): Investigate if we can have feature active during animations.
- mInputWindowHandleWrapper.setToken(null);
+ mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE,
+ InputConfig.NOT_TOUCHABLE);
} else {
- mInputWindowHandleWrapper.setToken(mToken);
+ mInputWindowHandleWrapper.setInputConfigMasked(0, InputConfig.NOT_TOUCHABLE);
}
return mInputWindowHandleWrapper;
}
@@ -115,58 +95,8 @@ class ActivityRecordInputSink {
inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
inputWindowHandle.ownerUid = Process.myUid();
inputWindowHandle.ownerPid = Process.myPid();
- inputWindowHandle.dispatchingTimeoutMillis =
- InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
+ inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.NO_INPUT_CHANNEL;
return inputWindowHandle;
}
- private InputEventReceiver createInputEventReceiver(InputChannel inputChannel) {
- return new SinkInputEventReceiver(inputChannel,
- mActivityRecord.mAtmService.mUiHandler.getLooper());
- }
-
- private void showAsToastAndLog(String message) {
- Toast.makeText(mActivityRecord.mAtmService.mUiContext, message,
- Toast.LENGTH_LONG).show();
- Slog.wtf(TAG, message + " " + mActivityRecord.mActivityComponent);
- }
-
- private class SinkInputEventReceiver extends InputEventReceiver {
- private long mLastToast = 0;
-
- SinkInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- public void onInputEvent(InputEvent event) {
- if (!(event instanceof MotionEvent)) {
- Slog.wtf(TAG,
- "Received InputEvent that was not a MotionEvent");
- finishInputEvent(event, true);
- return;
- }
- MotionEvent motionEvent = (MotionEvent) event;
- if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) {
- finishInputEvent(event, true);
- return;
- }
-
- if (event.getEventTime() - mLastToast > TOAST_COOL_DOWN_MILLIS) {
- String message = "go/activity-touch-opaque - "
- + mActivityRecord.mActivityComponent.getPackageName()
- + " blocked the touch!";
- showAsToastAndLog(message);
- mLastToast = event.getEventTime();
- mRapidTouchCount = 1;
- } else if (++mRapidTouchCount >= NUMBER_OF_TOUCHES_TO_DISABLE && !mDisabled) {
- // Disable touch blocking until Activity Record is recreated.
- String message = "Disabled go/activity-touch-opaque - "
- + mActivityRecord.mActivityComponent.getPackageName();
- showAsToastAndLog(message);
- mDisabled = true;
- }
- finishInputEvent(event, true);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7abcc4b808a1..e50aff438ecd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2687,11 +2687,8 @@ class ActivityStarter {
// launched into the same root task.
mTargetRootTask = Task.fromWindowContainerToken(mSourceRecord.mLaunchRootTask);
} else {
- final Task rootTask =
- getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions);
- // TODO(b/184806710): #getOrCreateRootTask should never return null?
- mTargetRootTask =
- rootTask != null ? rootTask : intentActivity.getRootTask();
+ mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask,
+ mOptions);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8b6262f0fcca..ad6f35452051 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5303,14 +5303,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/**
* Returns {@code true} if the process represented by the pid passed as argument is
- * instrumented.
+ * instrumented and the instrumentation source was granted with the permission also
+ * passed as argument.
*/
- boolean isInstrumenting(int pid) {
+ boolean instrumentationSourceHasPermission(int pid, String permission) {
final WindowProcessController process;
synchronized (mGlobalLock) {
process = mProcessMap.getProcess(pid);
}
- return process != null ? process.isInstrumenting() : false;
+ if (process == null || !process.isInstrumenting()) {
+ return false;
+ }
+ final int sourceUid = process.getInstrumentationSourceUid();
+ return checkPermission(permission, -1, sourceUid) == PackageManager.PERMISSION_GRANTED;
}
final class H extends Handler {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 05efb29cc775..5c09f09d9313 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -81,11 +81,11 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -130,6 +130,7 @@ import android.view.animation.TranslateAnimation;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TransitionAnimation;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils.Dump;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -237,7 +238,8 @@ public class AppTransition implements Dump {
mService = service;
mHandler = new Handler(service.mH.getLooper());
mDisplayContent = displayContent;
- mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG);
+ mTransitionAnimation = new TransitionAnimation(
+ context, ProtoLogImpl.isEnabled(WM_DEBUG_ANIM), TAG);
mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
@@ -1305,6 +1307,8 @@ public class AppTransition implements Dump {
pw.print(Integer.toHexString(mNextAppTransitionEnter));
pw.print(" mNextAppTransitionExit=0x");
pw.println(Integer.toHexString(mNextAppTransitionExit));
+ pw.print(" mNextAppTransitionBackgroundColor=0x");
+ pw.println(Integer.toHexString(mNextAppTransitionBackgroundColor));
}
switch (mNextAppTransitionType) {
case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f5ace6c78288..61cd22170e18 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -129,7 +129,6 @@ import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -3974,7 +3973,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean nonAppImeTargetAnimatingExit = mImeLayeringTarget.mAnimatingExit
&& mImeLayeringTarget.mAttrs.type != TYPE_BASE_APPLICATION
&& mImeLayeringTarget.isSelfAnimating(0, ANIMATION_TYPE_WINDOW_ANIMATION);
- if (mImeLayeringTarget.inAppOrRecentsTransition() || nonAppImeTargetAnimatingExit) {
+ if (mImeLayeringTarget.inTransitionSelfOrParent() || nonAppImeTargetAnimatingExit) {
showImeScreenshot();
}
}
@@ -4045,11 +4044,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private SurfaceControl createImeSurface(SurfaceControl.ScreenshotHardwareBuffer b,
Transaction t) {
final HardwareBuffer buffer = b.getHardwareBuffer();
- if (DEBUG_INPUT_METHOD) {
- Slog.d(TAG, "create IME snapshot for "
- + mImeTarget + ", buff width=" + buffer.getWidth()
- + ", height=" + buffer.getHeight());
- }
+ ProtoLog.i(WM_DEBUG_IME, "create IME snapshot for %s, buff width=%s, height=%s",
+ mImeTarget, buffer.getWidth(), buffer.getHeight());
final WindowState imeWindow = mImeTarget.getDisplayContent().mInputMethodWindow;
final ActivityRecord activity = mImeTarget.mActivityRecord;
final SurfaceControl imeParent = mImeTarget.mAttrs.type == TYPE_BASE_APPLICATION
@@ -4085,12 +4081,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mImeTarget.mAttrs.surfaceInsets.top);
t.setPosition(imeSurface, surfacePosition.x, surfacePosition.y);
}
+ ProtoLog.i(WM_DEBUG_IME, "Set IME snapshot position: (%d, %d)", surfacePosition.x,
+ surfacePosition.y);
return imeSurface;
}
private void removeImeSurface(Transaction t) {
if (mImeSurface != null) {
- if (DEBUG_INPUT_METHOD) Slog.d(TAG, "remove IME snapshot");
+ ProtoLog.i(WM_DEBUG_IME, "remove IME snapshot, caller=%s", Debug.getCallers(6));
t.remove(mImeSurface);
mImeSurface = null;
}
@@ -4120,9 +4118,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// to reflect the true IME insets visibility and the app task layout as possible.
if (isValidSnapshot
&& dc.getInsetsStateController().getImeSourceProvider().isImeShowing()) {
- if (DEBUG_INPUT_METHOD) {
- Slog.d(TAG, "show IME snapshot, ime target=" + mImeTarget);
- }
+ ProtoLog.i(WM_DEBUG_IME, "show IME snapshot, ime target=%s, callers=%s",
+ mImeTarget, Debug.getCallers(6));
t.show(mImeSurface);
} else if (!isValidSnapshot) {
removeImeSurface(t);
@@ -4168,7 +4165,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void removeImeScreenshotIfPossible() {
if (mImeLayeringTarget == null
|| mImeLayeringTarget.mAttrs.type != TYPE_APPLICATION_STARTING
- && !mImeLayeringTarget.inAppOrRecentsTransition()) {
+ && !mImeLayeringTarget.inTransitionSelfOrParent()) {
removeImeSurfaceImmediately();
}
}
@@ -4489,6 +4486,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* hierarchy.
*/
void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
+ if (mImeScreenshot != null) {
+ ProtoLog.i(WM_DEBUG_IME,
+ "onWindowAnimationFinished, wc=%s, type=%s, imeSnapshot=%s, target=%s",
+ wc, SurfaceAnimator.animationTypeToString(type), mImeScreenshot,
+ mImeScreenshot.getImeTarget());
+ }
if (mImeScreenshot != null && (wc == mImeScreenshot.getImeTarget()
|| wc.getWindow(w -> w == mImeScreenshot.getImeTarget()) != null)
&& (type & WindowState.EXIT_ANIMATING_TYPES) != 0) {
@@ -4876,6 +4879,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) {
super.setOrganizer(organizer, skipDisplayAreaAppeared);
mDisplayContent.updateImeParent();
+
+ // If the ImeContainer was previously unorganized then the framework might have
+ // reparented its surface control under an activity so we need to reparent it back
+ // under its parent.
+ if (organizer != null) {
+ final SurfaceControl imeParentSurfaceControl = getParentSurfaceControl();
+ if (mSurfaceControl != null && imeParentSurfaceControl != null) {
+ ProtoLog.i(WM_DEBUG_IME, "ImeContainer just became organized. Reparenting "
+ + "under parent. imeParentSurfaceControl=%s", imeParentSurfaceControl);
+ getPendingTransaction().reparent(mSurfaceControl, imeParentSurfaceControl);
+ } else {
+ ProtoLog.e(WM_DEBUG_IME, "ImeContainer just became organized but it doesn't "
+ + "have a parent or the parent doesn't have a surface control."
+ + " mSurfaceControl=%s imeParentSurfaceControl=%s",
+ mSurfaceControl, imeParentSurfaceControl);
+ }
+ }
}
}
@@ -5011,11 +5031,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
? mImeControlTarget.getWindow().mToken : null;
final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null
&& imeTarget.mToken == imeControlTargetToken
- && !imeTarget.inMultiWindowMode()
- // We don't need to set relative layer if the IME target in non-multi-window
- // mode is the activity main window since updateImeParent will ensure the IME
- // surface be attached on the fullscreen activity.
- && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION;
+ && !imeTarget.inMultiWindowMode();
if (canImeTargetSetRelativeLayer) {
mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
// TODO: We need to use an extra level on the app surface to ensure
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 88d7dff4ff1a..4573ede13f7f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -75,6 +75,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
@@ -83,7 +84,6 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -1366,8 +1366,7 @@ public class DisplayPolicy {
* @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
*/
int selectAnimation(WindowState win, int transit) {
- if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
- + ": transit=" + transit);
+ ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
if (win == mStatusBar) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -1460,7 +1459,7 @@ public class DisplayPolicy {
// with old content because home is easier to have different UI states.
return ANIMATION_NONE;
}
- if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT");
+ ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
return R.anim.app_starting_exit;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 262ddae02765..43ff58083657 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
@@ -32,7 +33,6 @@ import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATI
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
import static com.android.server.wm.DisplayRotationProto.ROTATION;
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
@@ -747,10 +747,11 @@ public class DisplayRotation {
final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully()
|| !mService.mPolicy.okToAnimate(false /* ignoreScreenOn */);
final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow();
- if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen="
- + topFullscreen + " rotationAnimation="
- + (topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation)
- + " forceJumpcut=" + forceJumpcut);
+ ProtoLog.i(WM_DEBUG_ANIM, "selectRotationAnimation topFullscreen=%s"
+ + " rotationAnimation=%d forceJumpcut=%b",
+ topFullscreen,
+ topFullscreen == null ? 0 : topFullscreen.getAttrs().rotationAnimation,
+ forceJumpcut);
if (forceJumpcut) {
mTmpRotationAnim.mExit = R.anim.rotation_animation_jump_exit;
mTmpRotationAnim.mEnter = R.anim.rotation_animation_enter;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 8866343afe03..0038c716fee7 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -145,6 +145,14 @@ final class LetterboxUiController {
return;
}
updateRoundedCorners(w);
+ // If there is another main window that is not an application-starting window, we should
+ // update rounded corners for it as well, to avoid flickering rounded corners.
+ final WindowState nonStartingAppW = mActivityRecord.findMainWindow(
+ /* includeStartingApp= */ false);
+ if (nonStartingAppW != null && nonStartingAppW != w) {
+ updateRoundedCorners(nonStartingAppW);
+ }
+
updateWallpaperForLetterbox(w);
if (shouldShowLetterboxUi(w)) {
if (mLetterbox == null) {
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 61f9fe29b2f8..d7e725b5e679 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -174,5 +174,9 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
void dumpDebugInner(ProtoOutputStream proto);
+
+ default WindowAnimationSpec asWindowAnimationSpec() {
+ return null;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index a4d338c06708..e21ae05c0827 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -281,6 +281,7 @@ public class RecentsAnimationController implements DeathRecipient {
task.setCanAffectSystemUiFlags(behindSystemBars);
}
}
+ InputMethodManagerInternal.get().maybeFinishStylusHandwriting();
if (!behindSystemBars) {
// Hiding IME if IME window is not attached to app.
// Since some windowing mode is not proper to snapshot Task with IME window
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index eeac230489f9..992778794203 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -110,7 +110,7 @@ class RemoteAnimationController implements DeathRecipient {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"goodToGo(): Animation canceled already");
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled("already_cancelled");
return;
}
@@ -129,7 +129,7 @@ class RemoteAnimationController implements DeathRecipient {
"goodToGo(): No apps to animate, mPendingAnimations=%d",
mPendingAnimations.size());
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled("no_app_targets");
return;
}
@@ -169,7 +169,7 @@ class RemoteAnimationController implements DeathRecipient {
mCanceled = true;
}
onAnimationFinished();
- invokeAnimationCancelled();
+ invokeAnimationCancelled(reason);
}
private void writeStartDebugStatement() {
@@ -296,7 +296,8 @@ class RemoteAnimationController implements DeathRecipient {
ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
}
- private void invokeAnimationCancelled() {
+ private void invokeAnimationCancelled(String reason) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
try {
mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 94fc51dc94d2..cc99f377bfee 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2754,7 +2754,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// First preference goes to the launch root task set in the activity options.
if (options != null) {
final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());
- if (canLaunchOnDisplay(r, candidateRoot)) {
+ if (candidateRoot != null && canLaunchOnDisplay(r, candidateRoot)) {
return candidateRoot;
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 92e2ee6f8af9..b57670914c11 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -26,13 +26,22 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,6 +49,8 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.server.AnimationThread;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Supplier;
/**
@@ -73,6 +84,10 @@ class SurfaceAnimationRunner {
@GuardedBy("mLock")
@VisibleForTesting
+ final ArrayMap<SurfaceControl, RunningAnimation> mPreProcessingAnimations = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
@GuardedBy("mLock")
@@ -136,23 +151,64 @@ class SurfaceAnimationRunner {
synchronized (mLock) {
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
- mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
- mChoreographer.postFrameCallback(this::startAnimations);
+ boolean requiresEdgeExtension = requiresEdgeExtension(a);
+
+ if (requiresEdgeExtension) {
+ mPreProcessingAnimations.put(animationLeash, runningAnim);
+
+ // We must wait for t to be committed since otherwise the leash doesn't have the
+ // windows we want to screenshot and extend as children.
+ t.addTransactionCommittedListener(Runnable::run, () -> {
+ final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();
+ final Runnable cleanUpEdgeExtension = edgeExtendWindow(animationLeash,
+ animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
+ mFrameTransaction);
+
+ runningAnim.mFinishCallback = () -> {
+ cleanUpEdgeExtension.run();
+ finishCallback.run();
+ };
+
+ synchronized (mLock) {
+ // only run if animation is not yet canceled by this point
+ if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
+ mPreProcessingAnimations.remove(animationLeash);
+ mPendingAnimations.put(animationLeash, runningAnim);
+ if (!mAnimationStartDeferred) {
+ mChoreographer.postFrameCallback(this::startAnimations);
+ }
+ }
+ }
+ });
}
- // Some animations (e.g. move animations) require the initial transform to be applied
- // immediately.
- applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
+ if (!requiresEdgeExtension) {
+ mPendingAnimations.put(animationLeash, runningAnim);
+ if (!mAnimationStartDeferred) {
+ mChoreographer.postFrameCallback(this::startAnimations);
+ }
+
+ // Some animations (e.g. move animations) require the initial transform to be
+ // applied immediately.
+ applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
+ }
}
}
+ private boolean requiresEdgeExtension(AnimationSpec a) {
+ return a.asWindowAnimationSpec() != null && a.asWindowAnimationSpec().hasExtension();
+ }
+
void onAnimationCancelled(SurfaceControl leash) {
synchronized (mLock) {
if (mPendingAnimations.containsKey(leash)) {
mPendingAnimations.remove(leash);
return;
}
+ if (mPreProcessingAnimations.containsKey(leash)) {
+ mPreProcessingAnimations.remove(leash);
+ return;
+ }
final RunningAnimation anim = mRunningAnimations.get(leash);
if (anim != null) {
mRunningAnimations.remove(leash);
@@ -264,10 +320,133 @@ class SurfaceAnimationRunner {
mApplyScheduled = false;
}
+ private Runnable edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a,
+ Transaction transaction) {
+ final Transformation transformationAtStart = new Transformation();
+ a.getTransformationAt(0, transformationAtStart);
+ final Transformation transformationAtEnd = new Transformation();
+ a.getTransformationAt(1, transformationAtEnd);
+
+ // We want to create an extension surface that is the maximal size and the animation will
+ // take care of cropping any part that overflows.
+ final Insets maxExtensionInsets = Insets.min(
+ transformationAtStart.getInsets(), transformationAtEnd.getInsets());
+
+ final int targetSurfaceHeight = bounds.height();
+ final int targetSurfaceWidth = bounds.width();
+
+ final List<SurfaceControl> extensionSurfaces = new ArrayList<>();
+
+ if (maxExtensionInsets.left < 0) {
+ final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.left, targetSurfaceHeight);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = 0;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Left Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ if (maxExtensionInsets.top < 0) {
+ final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.top);
+ final int xPos = 0;
+ final int yPos = maxExtensionInsets.top;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Top Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ if (maxExtensionInsets.right < 0) {
+ final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.right, targetSurfaceHeight);
+ final int xPos = targetSurfaceWidth;
+ final int yPos = 0;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Right Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ if (maxExtensionInsets.bottom < 0) {
+ final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.bottom);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = targetSurfaceHeight;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Bottom Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ Runnable cleanUp = () -> {
+ for (final SurfaceControl extensionSurface : extensionSurfaces) {
+ if (extensionSurface != null) {
+ transaction.remove(extensionSurface);
+ }
+ }
+ };
+
+ return cleanUp;
+ }
+
+ private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
+ Rect extensionRect, int xPos, int yPos, String layerName,
+ Transaction startTransaction) {
+ final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
+ .setName(layerName)
+ .setParent(surfaceToExtend)
+ .setHidden(true)
+ .setCallsite("DefaultTransitionHandler#startAnimation")
+ .setOpaque(true)
+ .setBufferSize(extensionRect.width(), extensionRect.height())
+ .build();
+
+ SurfaceControl.LayerCaptureArgs captureArgs =
+ new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ .setSourceCrop(edgeBounds)
+ .setFrameScale(1)
+ .setPixelFormat(PixelFormat.RGBA_8888)
+ .setChildrenOnly(true)
+ .setAllowProtected(true)
+ .build();
+ final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
+ SurfaceControl.captureLayers(captureArgs);
+
+ if (edgeBuffer == null) {
+ Log.e("SurfaceAnimationRunner", "Failed to create edge extension - "
+ + "edge buffer is null");
+ return null;
+ }
+
+ android.graphics.BitmapShader shader =
+ new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
+ android.graphics.Shader.TileMode.CLAMP,
+ android.graphics.Shader.TileMode.CLAMP);
+ final Paint paint = new Paint();
+ paint.setShader(shader);
+
+ final Surface surface = new Surface(edgeExtensionLayer);
+ Canvas c = surface.lockHardwareCanvas();
+ c.drawRect(extensionRect, paint);
+ surface.unlockCanvasAndPost(c);
+ surface.release();
+
+ startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
+ startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
+ startTransaction.setVisibility(edgeExtensionLayer, true);
+
+ return edgeExtensionLayer;
+ }
+
private static final class RunningAnimation {
final AnimationSpec mAnimSpec;
final SurfaceControl mLeash;
- final Runnable mFinishCallback;
+ Runnable mFinishCallback;
ValueAnimator mAnim;
@GuardedBy("mCancelLock")
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 53e33781bca6..fbf04262cc37 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -32,8 +32,11 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
@@ -185,10 +188,16 @@ class SurfaceAnimator {
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
- if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
+ ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
return;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mAnimation.dump(pw, "");
+ ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);
+ }
if (snapshotAnim != null) {
mSnapshot = freezer.takeSnapshotForAnimation();
if (mSnapshot == null) {
@@ -340,7 +349,8 @@ class SurfaceAnimator {
* to another animator.
*/
private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) {
- if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
+ ProtoLog.i(WM_DEBUG_ANIM, "Cancelling animation restarting=%b for %s",
+ restarting, mAnimatable);
final SurfaceControl leash = mLeash;
final AnimationAdapter animation = mAnimation;
final @AnimationType int animationType = mAnimationType;
@@ -419,7 +429,8 @@ class SurfaceAnimator {
final boolean reparent = surface != null && (curAnimationLeash == null
|| curAnimationLeash.equals(leash));
if (reparent) {
- if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
+ ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to original parent: %s for %s",
+ parent, animatable);
// We shouldn't really need these isValid checks but we do
// b/130364451
if (surface.isValid() && parent != null && parent.isValid()) {
@@ -444,7 +455,7 @@ class SurfaceAnimator {
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
boolean hidden, Supplier<Transaction> transactionFactory) {
- if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
+ ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
.setName(surface + " - animation-leash of " + animationTypeToString(type))
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c0bc0191b16d..23df429c3f24 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -405,6 +405,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+ mResumedActivity + " to:" + r + " reason:" + reason);
}
+
+ if (r != null && mResumedActivity == null) {
+ // Task is becoming active.
+ getTask().touchActiveTime();
+ }
+
final ActivityRecord prevR = mResumedActivity;
mResumedActivity = r;
mTaskSupervisor.updateTopResumedActivityIfNeeded();
@@ -518,7 +524,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|| isAllowedToEmbedActivityInTrustedMode(a);
}
+ /**
+ * Checks if the organized task fragment is allowed to embed activity in untrusted mode.
+ */
boolean isAllowedToEmbedActivityInUntrustedMode(@NonNull ActivityRecord a) {
+ final WindowContainer parent = getParent();
+ if (parent == null || !parent.getBounds().contains(getBounds())) {
+ // Without full trust between the host and the embedded activity, we don't allow
+ // TaskFragment to have bounds outside of the parent bounds.
+ return false;
+ }
return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index ff5bfbee61f4..7d738241e4a3 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -67,7 +67,8 @@ import java.util.function.Consumer;
class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
- private class DeathRecipient implements IBinder.DeathRecipient {
+ @VisibleForTesting
+ class DeathRecipient implements IBinder.DeathRecipient {
ITaskOrganizer mTaskOrganizer;
DeathRecipient(ITaskOrganizer organizer) {
@@ -77,7 +78,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
@Override
public void binderDied() {
synchronized (mGlobalLock) {
- final TaskOrganizerState state = mTaskOrganizerStates.remove(
+ final TaskOrganizerState state = mTaskOrganizerStates.get(
mTaskOrganizer.asBinder());
if (state != null) {
state.dispose();
@@ -170,7 +171,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- private class TaskOrganizerState {
+ @VisibleForTesting
+ class TaskOrganizerState {
private final TaskOrganizerCallbacks mOrganizer;
private final DeathRecipient mDeathRecipient;
private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
@@ -191,6 +193,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mUid = uid;
}
+ @VisibleForTesting
+ DeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -265,7 +272,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Remove organizer state after removing tasks so we get a chance to send
// onTaskVanished.
- mTaskOrganizerStates.remove(asBinder());
+ mTaskOrganizerStates.remove(mOrganizer.getBinder());
}
void unlinkDeath() {
@@ -596,7 +603,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
private void onTaskVanishedInternal(ITaskOrganizer organizer, Task task) {
for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
PendingTaskEvent entry = mPendingTaskEvents.get(i);
- if (task.mTaskId == entry.mTask.mTaskId) {
+ if (task.mTaskId == entry.mTask.mTaskId && entry.mTaskOrg == organizer) {
// This task is vanished so remove all pending event of it.
mPendingTaskEvents.remove(i);
if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) {
@@ -693,9 +700,16 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
break;
case PendingTaskEvent.EVENT_VANISHED:
- state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
- if (state != null) {
- state.mOrganizer.onTaskVanished(task);
+ // TaskOrganizerState cannot be used here because it might have already been
+ // removed.
+ // The state is removed when an organizer dies or is unregistered. In order to
+ // send the pending vanished task events, the mTaskOrg from event is used.
+ // These events should not ideally be sent and will be removed as part of
+ // b/224812558.
+ try {
+ event.mTaskOrg.onTaskVanished(task.getTaskInfo());
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Exception sending onTaskVanished callback", ex);
}
mLastSentTaskInfos.remove(task);
break;
@@ -1045,4 +1059,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
pw.println();
}
+
+ @VisibleForTesting
+ TaskOrganizerState getTaskOrganizerState(IBinder taskOrganizer) {
+ return mTaskOrganizerStates.get(taskOrganizer);
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8840cd557de6..18851b34ec04 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -243,9 +243,13 @@ class TransitionController {
return isCollecting() || isPlaying();
}
- /** @return {@code true} if wc is in a participant subtree */
+ /** @return {@code true} if a transition is running in a participant subtree of wc */
boolean inTransition(@NonNull WindowContainer wc) {
- if (isCollecting(wc)) return true;
+ if (isCollecting()) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (isCollecting(p)) return true;
+ }
+ }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
for (WindowContainer p = wc; p != null; p = p.getParent()) {
if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 5bfd546f7620..b6d668dde9d8 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -21,6 +21,7 @@ import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
@@ -75,6 +76,11 @@ public class WindowAnimationSpec implements AnimationSpec {
}
@Override
+ public WindowAnimationSpec asWindowAnimationSpec() {
+ return this;
+ }
+
+ @Override
public boolean getShowWallpaper() {
return mAnimation.getShowWallpaper();
}
@@ -89,11 +95,27 @@ public class WindowAnimationSpec implements AnimationSpec {
return mAnimation.getBackgroundColor();
}
+ /**
+ * @return If a window animation has outsets applied to it.
+ * @see Animation#hasExtension()
+ */
+ public boolean hasExtension() {
+ return mAnimation.hasExtension();
+ }
+
@Override
public long getDuration() {
return mAnimation.computeDurationHint();
}
+ public Rect getRootTaskBounds() {
+ return mRootTaskBounds;
+ }
+
+ public Animation getAnimation() {
+ return mAnimation;
+ }
+
@Override
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
final TmpValues tmp = mThreadLocalTmps.get();
@@ -106,7 +128,9 @@ public class WindowAnimationSpec implements AnimationSpec {
boolean cropSet = false;
if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
if (tmp.transformation.hasClipRect()) {
- t.setWindowCrop(leash, tmp.transformation.getClipRect());
+ final Rect clipRect = tmp.transformation.getClipRect();
+ accountForExtension(tmp.transformation, clipRect);
+ t.setWindowCrop(leash, clipRect);
cropSet = true;
}
} else {
@@ -114,6 +138,7 @@ public class WindowAnimationSpec implements AnimationSpec {
if (tmp.transformation.hasClipRect()) {
mTmpRect.intersect(tmp.transformation.getClipRect());
}
+ accountForExtension(tmp.transformation, mTmpRect);
t.setWindowCrop(leash, mTmpRect);
cropSet = true;
}
@@ -125,6 +150,14 @@ public class WindowAnimationSpec implements AnimationSpec {
}
}
+ private void accountForExtension(Transformation transformation, Rect clipRect) {
+ Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ if (!extensionInsets.equals(Insets.NONE)) {
+ // Extend the surface to allow for the edge extension to be visible
+ clipRect.inset(extensionInsets);
+ }
+ }
+
@Override
public long calculateStatusBarTransitionStartTime() {
TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e7b4e834eb5a..99e39f1969e1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -33,6 +33,7 @@ import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -47,7 +48,6 @@ import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -59,10 +59,8 @@ import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -107,6 +105,7 @@ import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -1175,27 +1174,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mTransitionController.inTransition(this);
}
- boolean inAppOrRecentsTransition() {
- if (!mTransitionController.isShellTransitionsEnabled()) {
- return isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
- }
- for (WindowContainer p = this; p != null; p = p.getParent()) {
- if (mTransitionController.isCollecting(p)) {
- return true;
- }
- }
- if (inTransition() || mTransitionController.inRecentsTransition(this)) return true;
-
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- WindowContainer child = mChildren.get(i);
- if (child.inAppOrRecentsTransition()) {
- return true;
- }
- }
- return false;
- }
-
boolean isExitAnimationRunningSelfOrChild() {
if (!mTransitionController.isShellTransitionsEnabled()) {
return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES);
@@ -2748,9 +2726,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
- if (DEBUG_ANIM) {
- Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
- }
+ ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
+ this, type, anim);
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
@@ -3119,9 +3096,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
a.restrictDuration(MAX_APP_TRANSITION_DURATION);
}
- if (DEBUG_ANIM) {
- logWithStack(TAG, "Loaded animation " + a + " for " + this
- + ", duration: " + ((a != null) ? a.getDuration() : 0));
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ ProtoLog.i(WM_DEBUG_ANIM, "Loaded animation %s for %s, duration: %d, stack=%s",
+ a, this, ((a != null) ? a.getDuration() : 0), Debug.getCallers(20));
}
final int containingWidth = frame.width();
final int containingHeight = frame.height();
@@ -3893,7 +3870,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
try {
overlay.getRemoteInterface().onConfigurationChanged(getConfiguration());
} catch (Exception e) {
- Slog.e(TAG, "Error sending initial configuration change to WindowContainer overlay");
+ ProtoLog.e(WM_DEBUG_ANIM,
+ "Error sending initial configuration change to WindowContainer overlay");
removeTrustedOverlay(overlay);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index c95470071e2d..42b556f77ab6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -34,7 +34,6 @@ public class WindowManagerDebugConfig {
static final String TAG_WM = "WindowManager";
static final boolean DEBUG = false;
- static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 43e84ae91118..dd2941c75346 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -2560,7 +2561,9 @@ public class WindowManagerService extends IWindowManager.Stub
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
+ String reason = null;
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
+ reason = "applyAnimation";
focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.mDisplayContent.okToAnimate() && win.isExitAnimationRunningSelfOrParent()) {
@@ -2570,6 +2573,7 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (win.mDisplayContent.okToAnimate()
&& win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)
&& win.mAttrs.type != TYPE_NOTIFICATION_SHADE) {
+ reason = "isWallpaperTarget";
// If the wallpaper is currently behind this app window, we need to change both of them
// inside of a transaction to avoid artifacts.
// For NotificationShade, sysui is in charge of running window animation and it updates
@@ -2585,6 +2589,10 @@ public class WindowManagerService extends IWindowManager.Stub
win.mDestroying = true;
win.destroySurface(false, stopped);
}
+ if (reason != null) {
+ ProtoLog.d(WM_DEBUG_ANIM, "Set animatingExit: reason=startExitingAnimation/%s win=%s",
+ reason, win);
+ }
if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.onWindowTransition(win, transit);
}
@@ -3760,7 +3768,8 @@ public class WindowManagerService extends IWindowManager.Stub
* Sets the touch mode state.
*
* To be able to change touch mode state, the caller must either own the focused window, or must
- * have the MODIFY_TOUCH_MODE_STATE permission. Instrumented processes are allowed to switch
+ * have the {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE} permission. Instrumented
+ * process, sourced with {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE}, may switch
* touch mode at any time.
*
* @param mode the touch mode to set
@@ -3773,9 +3782,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
-
- final boolean hasPermission = mAtmService.isInstrumenting(pid)
- || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()");
+ final boolean hasPermission =
+ mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE)
+ || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()");
final long token = Binder.clearCallingIdentity();
try {
if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 044da3982e26..fe5f6759a17a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -19,8 +19,6 @@ package com.android.server.wm;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.isStartResultSuccessful;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
-import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -681,7 +679,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
}
- effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
+ effects |= deleteTaskFragment(taskFragment, organizer, errorCallbackToken);
break;
}
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -699,8 +697,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) {
- sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
- errorCallbackToken,
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
convertStartFailureToThrowable(result, activityIntent));
} else {
effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -710,13 +707,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
final IBinder fragmentToken = hop.getNewParent();
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
- if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+ final TaskFragment parent = mLaunchTaskFragments.get(fragmentToken);
+ if (parent == null || activity == null) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to operate with invalid fragment token or activity.");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
- activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ if (!parent.isAllowedToEmbedActivity(activity)) {
+ final Throwable exception = new SecurityException(
+ "The task fragment is not trusted to embed the given activity.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ activity.reparent(parent, POSITION_TOP);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
@@ -860,12 +864,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final WindowContainer newParent = hop.getNewParent() != null
? WindowContainer.fromBinder(hop.getNewParent())
: null;
- if (oldParent == null || !oldParent.isAttached()) {
+ if (oldParent == null || oldParent.asTaskFragment() == null
+ || !oldParent.isAttached()) {
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ oldParent);
break;
}
- reparentTaskFragment(oldParent, newParent, errorCallbackToken);
+ reparentTaskFragment(oldParent.asTaskFragment(), newParent, organizer,
+ errorCallbackToken);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
@@ -1246,7 +1252,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.enforceTaskPermission(func);
}
- private void enforceTaskPermission(String func, WindowContainerTransaction t) {
+ private void enforceTaskPermission(String func, @Nullable WindowContainerTransaction t) {
if (t == null || t.getTaskFragmentOrganizer() == null) {
enforceTaskPermission(func);
return;
@@ -1271,14 +1277,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
// Only allow to apply changes to TaskFragment that is created by this organizer.
- WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
+ final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
enforceTaskFragmentOrganized(func, wc, organizer);
enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
}
- // TODO(b/197364677): Enforce safety of hierarchy operations in untrusted mode. E.g. one
- // could first change a trusted TF, and then start/reparent untrusted activity there.
-
// Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
for (int i = hops.size() - 1; i >= 0; i--) {
@@ -1352,8 +1355,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
* Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
* parent bounds are not allowed for embedding without full trust between the host and the
* target.
- * TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
- * tapjacking.
*/
private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
@@ -1361,35 +1362,48 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
return;
}
- // Check if TaskFragment is embedded in fully trusted mode
- if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
- // Fully trusted, no need to check further
- return;
- }
-
if (change == null) {
return;
}
final int changeMask = change.getChangeMask();
- if ((changeMask & (CHANGE_BOUNDS_TRANSACTION | CHANGE_BOUNDS_TRANSACTION_RECT)) != 0) {
+ if (changeMask != 0) {
+ // None of the change should be requested from a TaskFragment organizer.
String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " trying to apply SurfaceControl changes to TaskFragment in non-trusted "
- + "embedding mode, TaskFragmentOrganizer=" + organizer;
+ + " trying to apply changes of " + changeMask + " to TaskFragment"
+ + " TaskFragmentOrganizer=" + organizer;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- if (change.getWindowSetMask() == 0) {
- // Nothing else to check.
+ // Check if TaskFragment is embedded in fully trusted mode.
+ if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
+ // Fully trusted, no need to check further
return;
}
- WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration;
- WindowContainer wcParent = wc.getParent();
+ final WindowContainer wcParent = wc.getParent();
if (wcParent == null) {
- Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
+ Slog.e(TAG, "Attempt to apply config change on task fragment that has no parent");
+ return;
+ }
+ final Configuration requestedConfig = change.getConfiguration();
+ final Configuration parentConfig = wcParent.getConfiguration();
+ if (parentConfig.screenWidthDp < requestedConfig.screenWidthDp
+ || parentConfig.screenHeightDp < requestedConfig.screenHeightDp
+ || parentConfig.smallestScreenWidthDp < requestedConfig.smallestScreenWidthDp) {
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply screen width/height greater than parent's for non-trusted"
+ + " host, TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (change.getWindowSetMask() == 0) {
+ // No bounds change.
return;
}
- if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
+ final WindowConfiguration requestedWindowConfig = requestedConfig.windowConfiguration;
+ final WindowConfiguration parentWindowConfig = parentConfig.windowConfiguration;
+ if (!parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) {
String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " trying to apply bounds outside of parent for non-trusted host,"
@@ -1397,6 +1411,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+ if (requestedWindowConfig.getAppBounds() != null
+ && parentWindowConfig.getAppBounds() != null
+ && !parentWindowConfig.getAppBounds().contains(
+ requestedWindowConfig.getAppBounds())) {
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply app bounds outside of parent for non-trusted host,"
+ + " TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
}
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@@ -1422,7 +1447,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|| ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception =
- new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ new SecurityException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
@@ -1439,33 +1464,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
}
- void reparentTaskFragment(@NonNull WindowContainer oldParent,
- @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) {
- WindowContainer parent = newParent;
- if (parent == null && oldParent.asTaskFragment() != null) {
- parent = oldParent.asTaskFragment().getTask();
+ void reparentTaskFragment(@NonNull TaskFragment oldParent, @Nullable WindowContainer newParent,
+ @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
+ final TaskFragment newParentTF;
+ if (newParent == null) {
+ // Use the old parent's parent if the caller doesn't specify the new parent.
+ newParentTF = oldParent.getTask();
+ } else {
+ newParentTF = newParent.asTaskFragment();
}
- if (parent == null) {
+ if (newParentTF == null) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with invalid container");
- sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(),
- errorCallbackToken, exception);
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
+ if (newParentTF.getTaskFragmentOrganizer() != null) {
+ // We are reparenting activities to a new embedded TaskFragment, this operation is only
+ // allowed if the new parent is trusted by all reparent activities.
+ final boolean isEmbeddingDisallowed = oldParent.forAllActivities(activity ->
+ !newParentTF.isAllowedToEmbedActivity(activity));
+ if (isEmbeddingDisallowed) {
+ final Throwable exception = new SecurityException(
+ "The new parent is not trusted to embed the activities.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ return;
+ }
+ }
while (oldParent.hasChild()) {
- oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
+ oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
}
}
private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
- @Nullable IBinder errorCallbackToken) {
+ @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
if (index < 0) {
final Throwable exception =
new IllegalArgumentException("Not allowed to operate with invalid "
+ "taskFragment");
- sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
- errorCallbackToken, exception);
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return 0;
}
mLaunchTaskFragments.removeAt(index);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bb7876734260..0ca1058a80b2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -106,6 +106,7 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -140,7 +141,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -252,6 +252,7 @@ import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
@@ -262,6 +263,7 @@ import com.android.server.wm.SurfaceAnimator.AnimationType;
import dalvik.annotation.optimization.NeverCompile;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
@@ -2241,6 +2243,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Mark all relevant flags for that onExitAnimationDone will proceed all the way
// to actually remove it.
if (!visible && isVisibleNow && mActivityRecord.isAnimating(PARENTS | TRANSITION)) {
+ ProtoLog.d(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=onAppVisibilityChanged win=%s", this);
mAnimatingExit = true;
mRemoveOnExit = true;
mWindowRemovalAllowed = true;
@@ -2580,6 +2584,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// been removed. We probably need another flag to indicate that window removal
// should be deffered vs. overloading the flag that says we are playing an exit
// animation.
+ ProtoLog.v(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=remove/replaceWindow win=%s", this);
mAnimatingExit = true;
mReplacingRemoveRequested = true;
return;
@@ -2608,6 +2614,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Try starting an animation.
if (mWinAnimator.applyAnimationLocked(transit, false)) {
+ ProtoLog.v(WM_DEBUG_ANIM,
+ "Set animatingExit: reason=remove/applyAnimation win=%s", this);
mAnimatingExit = true;
// mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
@@ -2634,6 +2642,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// The exit animation is running or should run... wait for it!
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"Not removing %s due to exit animation", this);
+ ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/isAnimating win=%s",
+ this);
setupWindowForRemoveOnExit();
if (mActivityRecord != null) {
mActivityRecord.updateReportedVisibilityLocked();
@@ -3593,6 +3603,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Clear animating flags now, since the surface is now gone. (Note this is true even
// if the surface is saved, to outside world the surface is still NO_SURFACE.)
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
if (useBLASTSync()) {
immediatelyNotifyBlastSync();
@@ -3869,25 +3880,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outFrames.displayFrame.scale(mInvGlobalScale);
}
- final Rect backdropFrame = outFrames.backdropFrame;
- // When the task is docked, we send fullscreen sized backdropFrame as soon as resizing
- // start even if we haven't received the relayout window, so that the client requests
- // the relayout sooner. When dragging stops, backdropFrame needs to stay fullscreen
- // until the window to small size, otherwise the multithread renderer will shift last
- // one or more frame to wrong offset. So here we send fullscreen backdrop if either
- // isDragResizing() or isDragResizeChanged() is true.
- final boolean resizing = isDragResizing() || isDragResizeChanged();
- if (!resizing || getWindowConfiguration().useWindowFrameForBackdrop()) {
- // Surface position is now inherited from parent, and BackdropFrameRenderer uses
- // backdrop frame to position content. Thus we just keep the size of backdrop frame,
- // and remove the offset to avoid double offset from display origin.
- backdropFrame.set(outFrames.frame);
- backdropFrame.offsetTo(0, 0);
- } else {
- final DisplayInfo displayInfo = getDisplayInfo();
- backdropFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- }
-
// Note: in the cases where the window is tied to an activity, we should not send a
// configuration update when the window has requested to be hidden. Doing so can lead to
// the client erroneously accepting a configuration that would have otherwise caused an
@@ -4682,8 +4674,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
- if (DEBUG_ANIM) Slog.v(TAG,
- "performShowLocked: mDrawState=HAS_DRAWN in " + this);
+ ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
mWinAnimator.mDrawState = HAS_DRAWN;
mWmService.scheduleAnimationLocked();
@@ -5003,7 +4994,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
boolean isExitAnimationRunningSelfOrParent() {
- return inAppOrRecentsTransition()
+ return inTransitionSelfOrParent()
|| isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
@@ -5011,6 +5002,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
}
+ /**
+ * @return {@code true} if self or the parent container of the window is in transition.
+ * (e.g. The app or recents transition)
+ */
+ boolean inTransitionSelfOrParent() {
+ if (!mTransitionController.isShellTransitionsEnabled()) {
+ return isAnimating(PARENTS | TRANSITION,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ }
+ return mTransitionController.inTransition(this);
+ }
+
private boolean shouldFinishAnimatingExit() {
// Exit animation might be applied soon.
if (inTransition()) {
@@ -5054,9 +5057,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void onExitAnimationDone() {
- if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
- + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
- + " selfAnimating=" + isAnimating());
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ final AnimationAdapter animationAdapter = mSurfaceAnimator.getAnimation();
+ StringWriter sw = new StringWriter();
+ if (animationAdapter != null) {
+ PrintWriter pw = new PrintWriter(sw);
+ animationAdapter.dump(pw, "");
+ }
+ ProtoLog.v(WM_DEBUG_ANIM, "onExitAnimationDone in %s"
+ + ": exiting=%b remove=%b selfAnimating=%b anim=%s",
+ this, mAnimatingExit, mRemoveOnExit, isAnimating(), sw);
+ }
if (!mChildren.isEmpty()) {
// Copying to a different list as multiple children can be removed.
@@ -5110,6 +5121,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=exitAnimationDone win=%s", this);
getDisplayContent().mWallpaperController.hideWallpapers(this);
}
@@ -5141,6 +5153,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// show up with wrong position or scale.
if (mAnimatingExit) {
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=clearAnimatingFlags win=%s",
+ this);
didSomething = true;
}
if (mDestroying) {
@@ -5215,6 +5229,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
cancelAnimation();
}
mAnimatingExit = false;
+ ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=relayoutVisibleWindow win=%s",
+ this);
}
if (mDestroying) {
mDestroying = false;
@@ -5299,7 +5315,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
+ ProtoLog.v(WM_DEBUG_ANIM, "Setting move animation on %s", this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 285a6d5bdf5f..5c0557f2d1f4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
@@ -34,7 +35,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMAT
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
@@ -60,6 +60,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -178,10 +179,9 @@ class WindowStateAnimator {
void onAnimationFinished() {
// Done animating, clean up.
- if (DEBUG_ANIM) Slog.v(
- TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
- + ", reportedVisible="
- + (mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
+ ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s: exiting=%b, reportedVisible=%b",
+ this, mWin.mAnimatingExit,
+ (mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
@@ -264,9 +264,8 @@ class WindowStateAnimator {
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
- if (DEBUG_ANIM) {
- Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
- }
+ ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
+ mSurfaceController);
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
@@ -298,9 +297,7 @@ class WindowStateAnimator {
w.setHasSurface(false);
- if (DEBUG_ANIM) {
- Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING");
- }
+ ProtoLog.i(WM_DEBUG_ANIM, "createSurface %s: mDrawState=DRAW_PENDING", this);
resetDrawState();
@@ -503,8 +500,8 @@ class WindowStateAnimator {
}
}
} else {
- if (DEBUG_ANIM && mWin.isAnimating(TRANSITION | PARENTS)) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
+ if (mWin.isAnimating(TRANSITION | PARENTS)) {
+ ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this);
}
}
@@ -646,15 +643,12 @@ class WindowStateAnimator {
mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
- if (DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation: win=" + this
- + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
- + " a=" + a
- + " transit=" + transit
- + " type=" + mAttrType
- + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s"
+ + " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
+ this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20));
+ }
if (a != null) {
- if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
mWin.startAnimation(a);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
index bf78c67d5b57..82fe8b9362b0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -16,15 +16,12 @@
package com.android.server.devicepolicy;
-import static android.app.admin.DevicePolicyResources.Drawables.Style;
-
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyDrawableResource;
import android.app.admin.DevicePolicyResources;
-import android.app.admin.DevicePolicyResources.Drawables;
import android.app.admin.DevicePolicyStringResource;
import android.app.admin.ParcelableResource;
import android.os.Environment;
@@ -180,16 +177,18 @@ class DeviceManagementResourcesProvider {
@Nullable
ParcelableResource getDrawable(
String drawableId, String drawableStyle, String drawableSource) {
- if (mUpdatedDrawablesForSource.containsKey(drawableId)
- && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
- return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource);
- }
- if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
- Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
- return null;
- }
- if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) {
- return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
+ synchronized (mLock) {
+ if (mUpdatedDrawablesForSource.containsKey(drawableId)
+ && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
+ return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource);
+ }
+ if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
+ Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
+ return null;
+ }
+ if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) {
+ return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
+ }
}
Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
return null;
@@ -249,10 +248,11 @@ class DeviceManagementResourcesProvider {
@Nullable
ParcelableResource getString(String stringId) {
- if (mUpdatedStrings.containsKey(stringId)) {
- return mUpdatedStrings.get(stringId);
+ synchronized (mLock) {
+ if (mUpdatedStrings.containsKey(stringId)) {
+ return mUpdatedStrings.get(stringId);
+ }
}
-
Log.d(TAG, "No updated string found for string id " + stringId);
return null;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1052d1dead87..c5bb1bfb4663 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -114,6 +114,8 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHAN
import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_SOON_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
@@ -172,7 +174,6 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -7030,14 +7031,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private String getGenericWipeReason(
boolean calledByProfileOwnerOnOrgOwnedDevice, boolean calledOnParentInstance) {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
return calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance
- ? dpm.getResources().getString(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
- () -> mContext.getString(
- R.string.device_ownership_relinquished))
- : dpm.getResources().getString(WORK_PROFILE_DELETED_GENERIC_MESSAGE,
- () -> mContext.getString(
- R.string.work_profile_deleted_description_dpm_wipe));
+ ? getUpdatableString(
+ WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+ R.string.device_ownership_relinquished)
+ : getUpdatableString(
+ WORK_PROFILE_DELETED_GENERIC_MESSAGE,
+ R.string.work_profile_deleted_description_dpm_wipe);
}
/**
@@ -7133,9 +7133,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private String getWorkProfileDeletedTitle() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(WORK_PROFILE_DELETED_TITLE,
- () -> mContext.getString(R.string.work_profile_deleted));
+ return getUpdatableString(WORK_PROFILE_DELETED_TITLE, R.string.work_profile_deleted);
}
private void clearWipeProfileNotification() {
@@ -7449,10 +7447,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private String getFailedPasswordAttemptWipeMessage() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
- () -> mContext.getString(
- R.string.work_profile_deleted_reason_maximum_password_failure));
+ return getUpdatableString(
+ WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+ R.string.work_profile_deleted_reason_maximum_password_failure);
}
/**
@@ -12649,15 +12646,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private String getLocationChangedTitle() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(LOCATION_CHANGED_TITLE,
- () -> mContext.getString(R.string.location_changed_notification_title));
+ return getUpdatableString(
+ LOCATION_CHANGED_TITLE, R.string.location_changed_notification_title);
}
private String getLocationChangedText() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(LOCATION_CHANGED_MESSAGE,
- () -> mContext.getString(R.string.location_changed_notification_text));
+ return getUpdatableString(
+ LOCATION_CHANGED_MESSAGE, R.string.location_changed_notification_text);
}
@Override
@@ -13256,19 +13251,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slogf.e(LOG_TAG, "appLabel is inexplicably null");
return null;
}
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(
+ return getUpdatableString(
PRINTING_DISABLED_NAMED_ADMIN,
- () -> getDefaultPrintingDisabledMsg(appLabel),
+ R.string.printing_disabled_by,
appLabel);
}
}
- private String getDefaultPrintingDisabledMsg(CharSequence appLabel) {
- return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
- .getResources().getString(R.string.printing_disabled_by, appLabel);
- }
-
@Override
protected DevicePolicyCache getDevicePolicyCache() {
return mPolicyCache;
@@ -15875,15 +15864,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private String getNetworkLoggingTitle() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(NETWORK_LOGGING_TITLE,
- () -> mContext.getString(R.string.network_logging_notification_title));
+ return getUpdatableString(
+ NETWORK_LOGGING_TITLE, R.string.network_logging_notification_title);
}
private String getNetworkLoggingText() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(NETWORK_LOGGING_MESSAGE,
- () -> mContext.getString(R.string.network_logging_notification_text));
+ return getUpdatableString(
+ NETWORK_LOGGING_MESSAGE, R.string.network_logging_notification_text);
}
private void handleCancelNetworkLoggingNotification() {
@@ -17470,35 +17457,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private String getPersonalAppSuspensionButtonText() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
- () -> mContext.getString(R.string.personal_apps_suspended_turn_profile_on));
+ return getUpdatableString(
+ PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+ R.string.personal_apps_suspended_turn_profile_on);
}
private String getPersonalAppSuspensionTitle() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(PERSONAL_APP_SUSPENSION_TITLE,
- () -> mContext.getString(R.string.personal_apps_suspension_title));
+ return getUpdatableString(
+ PERSONAL_APP_SUSPENSION_TITLE, R.string.personal_apps_suspension_title);
}
private String getPersonalAppSuspensionText() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(PERSONAL_APP_SUSPENSION_TITLE,
- () -> mContext.getString(R.string.personal_apps_suspension_text));
+ return getUpdatableString(
+ PERSONAL_APP_SUSPENSION_MESSAGE, R.string.personal_apps_suspension_text);
}
private String getPersonalAppSuspensionSoonText(String date, String time, int maxDays) {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(PERSONAL_APP_SUSPENSION_TITLE,
- () -> mContext.getString(
- R.string.personal_apps_suspension_soon_text, date, time, maxDays),
+ return getUpdatableString(
+ PERSONAL_APP_SUSPENSION_SOON_MESSAGE, R.string.personal_apps_suspension_soon_text,
date, time, maxDays);
}
private String getWorkProfileContentDescription() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION,
- () -> mContext.getString(R.string.notification_work_profile_content_description));
+ return getUpdatableString(
+ NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION,
+ R.string.notification_work_profile_content_description);
}
@Override
@@ -18802,6 +18785,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private String getUpdatableString(
+ String updatableStringId, int defaultStringId, Object... formatArgs) {
+ ParcelableResource resource = mDeviceManagementResourcesProvider.getString(
+ updatableStringId);
+ if (resource == null) {
+ return ParcelableResource.loadDefaultString(() ->
+ mContext.getString(defaultStringId, formatArgs));
+ }
+ return resource.getString(
+ mContext, () -> mContext.getString(defaultStringId, formatArgs), formatArgs);
+ }
+
public boolean isDpcDownloaded() {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
@@ -18842,14 +18837,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public List<UserHandle> getPolicyManagedProfiles(@NonNull UserHandle user) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
int userId = user.getIdentifier();
return mInjector.binderWithCleanCallingIdentity(() -> {
List<UserInfo> userProfiles = mUserManager.getProfiles(userId);
List<UserHandle> result = new ArrayList<>();
for (int i = 0; i < userProfiles.size(); i++) {
- if (userProfiles.get(i).isManagedProfile() && hasProfileOwner(userId)) {
- result.add(new UserHandle(userProfiles.get(i).id));
+ UserInfo userInfo = userProfiles.get(i);
+ if (userInfo.isManagedProfile() && hasProfileOwner(userInfo.id)) {
+ result.add(new UserHandle(userInfo.id));
}
}
return result;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 0e96567dbfad..a49577b21957 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -111,6 +111,11 @@ static bool getAlwaysEnableReadTimeoutsForSystemDataLoaders() {
true);
}
+static bool getEnableReadTimeoutsAfterInstall() {
+ return android::base::GetBoolProperty("debug.incremental.enable_read_timeouts_after_install",
+ true);
+}
+
static bool getEnforceReadLogsMaxIntervalForSystemDataLoaders() {
return android::base::GetBoolProperty("debug.incremental.enforce_readlogs_max_interval_for_"
"system_dataloaders",
@@ -853,7 +858,7 @@ void IncrementalService::onInstallationComplete(StorageId storage) {
// Always enable long read timeouts after installation is complete.
std::unique_lock l(ifs->lock);
- ifs->setReadTimeoutsRequested(true);
+ ifs->setReadTimeoutsRequested(getEnableReadTimeoutsAfterInstall());
applyStorageParamsLocked(*ifs);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f7c66c5cff69..73f0a74c232e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1486,8 +1486,9 @@ public final class SystemServer implements Dumpable {
// TelecomLoader hooks into classes with defined HFP logic,
// so check for either telephony or microphone.
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELECOM)
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
t.traceBegin("StartTelecomLoaderService");
mSystemServiceManager.startService(TelecomLoaderService.class);
t.traceEnd();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index ca67bcb7f38c..e1fe1d8433ef 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -18,6 +18,7 @@ package com.android.server.midi;
import android.annotation.NonNull;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -151,6 +152,8 @@ public class MidiService extends IMidiManager.Stub {
private static final UUID MIDI_SERVICE = UUID.fromString(
"03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
+ private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>();
+
// PackageMonitor for listening to package changes
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
@@ -643,6 +646,55 @@ public class MidiService extends IMidiManager.Stub {
return false;
}
+ private static void dumpIntentExtras(Intent intent) {
+ String action = intent.getAction();
+ Log.d(TAG, "Intent: " + action);
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ for (String key : bundle.keySet()) {
+ Log.d(TAG, " " + key + " : "
+ + (bundle.get(key) != null ? bundle.get(key) : "NULL"));
+ }
+ }
+ }
+
+ private static boolean isBleTransport(Intent intent) {
+ Bundle bundle = intent.getExtras();
+ boolean isBle = false;
+ if (bundle != null) {
+ isBle = bundle.getInt(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_AUTO)
+ == BluetoothDevice.TRANSPORT_LE;
+ }
+ return isBle;
+ }
+
+ private void dumpUuids(BluetoothDevice btDevice) {
+ Log.d(TAG, "UUIDs for " + btDevice);
+
+ ParcelUuid[] uuidParcels = btDevice.getUuids();
+ if (uuidParcels == null) {
+ Log.d(TAG, "No UUID Parcels");
+ return;
+ }
+
+ for (ParcelUuid parcel : uuidParcels) {
+ UUID uuid = parcel.getUuid();
+ Log.d(TAG, " uuid:" + uuid);
+ }
+ }
+
+ private boolean hasNonMidiUuids(BluetoothDevice btDevice) {
+ ParcelUuid[] uuidParcels = btDevice.getUuids();
+ // The assumption is that these services are indicative of devices that
+ // ARE NOT MIDI devices.
+ for (ParcelUuid parcel : uuidParcels) {
+ if (mNonMidiUUIDs.contains(parcel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -655,10 +707,27 @@ public class MidiService extends IMidiManager.Stub {
switch (action) {
case BluetoothDevice.ACTION_ACL_CONNECTED: {
Log.d(TAG, "ACTION_ACL_CONNECTED");
+ dumpIntentExtras(intent);
+ // BLE-MIDI controllers are by definition BLE, so if this device
+ // isn't, it CAN'T be a midi device
+ if (!isBleTransport(intent)) {
+ Log.i(TAG, "No BLE transport - NOT MIDI");
+ break;
+ }
+
+ Log.d(TAG, "BLE Device");
BluetoothDevice btDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // We can't determine here if this is a BLD-MIDI device, so go ahead and try
- // to open as a MIDI device, further down it will get figured out.
+ dumpUuids(btDevice);
+
+ // See if there are any service UUIDs and if so do any of them indicate a
+ // Non-MIDI device (headset, headphones, QWERTY keyboard....)
+ if (hasNonMidiUuids(btDevice)) {
+ Log.d(TAG, "Non-MIDI service UUIDs found. NOT MIDI");
+ break;
+ }
+
+ Log.d(TAG, "Potential MIDI Device.");
openBluetoothDevice(btDevice);
}
break;
@@ -689,6 +758,17 @@ public class MidiService extends IMidiManager.Stub {
context.registerReceiver(mBleMidiReceiver, filter);
mBluetoothServiceUid = -1;
+
+ mNonMidiUUIDs.add(BluetoothUuid.A2DP_SINK); // Headphones?
+ mNonMidiUUIDs.add(BluetoothUuid.A2DP_SOURCE); // Headset?
+ mNonMidiUUIDs.add(BluetoothUuid.ADV_AUDIO_DIST);
+ mNonMidiUUIDs.add(BluetoothUuid.AVRCP_CONTROLLER);
+ mNonMidiUUIDs.add(BluetoothUuid.HFP);
+ mNonMidiUUIDs.add(BluetoothUuid.HSP);
+ mNonMidiUUIDs.add(BluetoothUuid.HID);
+ mNonMidiUUIDs.add(BluetoothUuid.LE_AUDIO);
+ mNonMidiUUIDs.add(BluetoothUuid.HOGP);
+ mNonMidiUUIDs.add(BluetoothUuid.HEARING_AID);
}
private void onUnlockUser() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index d44fb3bc2448..47d4c8d9af46 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -19,6 +19,8 @@ package com.android.server.am;
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.CAMERA;
+import static android.Manifest.permission.RECORD_AUDIO;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
@@ -26,6 +28,14 @@ import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTE
import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
import static android.app.ActivityManager.isLowRamDeviceStatic;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_ACTIVATE_PLATFORM_VPN;
+import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -301,21 +311,21 @@ public final class BackgroundRestrictionTest {
doReturn(new String[]{packageName})
.when(mPackageManager)
.getPackagesForUid(eq(uid));
- doReturn(AppOpsManager.MODE_IGNORED)
- .when(mAppOpsManager)
- .checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName);
- doReturn(AppOpsManager.MODE_IGNORED)
- .when(mAppOpsManager)
- .checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, uid, packageName);
+ final int[] ops = new int[] {
+ OP_ACTIVATE_VPN,
+ OP_ACTIVATE_PLATFORM_VPN,
+ OP_FINE_LOCATION,
+ OP_CAMERA,
+ OP_RECORD_AUDIO,
+ };
+ for (int op : ops) {
+ setAppOpState(packageName, uid, op, false);
+ }
final String[] permissions = new String[] {ACCESS_BACKGROUND_LOCATION,
- ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION};
+ ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, CAMERA, RECORD_AUDIO,
+ };
for (String permission : permissions) {
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, permission);
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, permission, userId);
+ setPermissionState(packageName, uid, permission, false);
}
}
doReturn(appStandbyInfoList).when(mAppStandbyInternal).getAppStandbyBuckets(userId);
@@ -1145,51 +1155,51 @@ public final class BackgroundRestrictionTest {
// Long-running FGS with type "location", but ran for a very short time.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_LOCATION, 0, null, null, null,
+ FOREGROUND_SERVICE_TYPE_LOCATION, 0, null, OP_NONE, null, null,
timeout(windowMs * 2).times(2));
// Long-running FGS with type "location", and ran for a while.
// We shouldn't see notifications in this case.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_LOCATION, thresholdMs * 2, null, null, null,
+ FOREGROUND_SERVICE_TYPE_LOCATION, thresholdMs * 2, null, OP_NONE, null, null,
timeout(windowMs * 2).times(0));
// Long-running FGS with background location permission.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_LOCATION, 0, ACCESS_BACKGROUND_LOCATION, null, null,
- timeout(windowMs * 2).times(0));
+ FOREGROUND_SERVICE_TYPE_LOCATION, 0, ACCESS_BACKGROUND_LOCATION, OP_NONE,
+ null, null, timeout(windowMs * 2).times(0));
// Long-running FGS with type "mediaPlayback", but ran for a very short time.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, null, null, null,
+ FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, null, OP_NONE, null, null,
timeout(windowMs * 2).times(2));
// Long-running FGS with type "mediaPlayback", and ran for a while.
// We shouldn't see notifications in this case.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, thresholdMs * 2, null, null, null,
- timeout(windowMs * 2).times(0));
+ FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, thresholdMs * 2, null, OP_NONE,
+ null, null, timeout(windowMs * 2).times(0));
// Long-running FGS with type "camera", and ran for a while.
// We shouldn't see notifications in this case.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_CAMERA, thresholdMs * 2, null, null, null,
+ FOREGROUND_SERVICE_TYPE_CAMERA, thresholdMs * 2, null, OP_NONE, null, null,
timeout(windowMs * 2).times(0));
// Long-running FGS with type "location|mediaPlayback", but ran for a very short time.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION | FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
- 0, null, null, null, timeout(windowMs * 2).times(2));
+ 0, null, OP_NONE, null, null, timeout(windowMs * 2).times(2));
// Long-running FGS with type "location|mediaPlayback", and ran for a while.
// We shouldn't see notifications in this case.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION | FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
- thresholdMs * 2, null, null, null, timeout(windowMs * 2).times(0));
+ thresholdMs * 2, null, OP_NONE, null, null, timeout(windowMs * 2).times(0));
// Long-running FGS with a media session starts/stops right away.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE,
List.of(Pair.create(createMediaControllers(
new String[] {testPkgName1}, new int[] {testUid1}), 0L)), null,
timeout(windowMs * 2).times(2));
@@ -1197,14 +1207,14 @@ public final class BackgroundRestrictionTest {
// Long-running FGS with media session, and ran for a while.
// We shouldn't see notifications in this case.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, thresholdMs * 2, null,
+ FOREGROUND_SERVICE_TYPE_NONE, thresholdMs * 2, null, OP_NONE,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), thresholdMs * 2)), null,
timeout(windowMs * 2).times(0));
// Long-running FGS with 2 media sessions start/stop right away
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE,
List.of(Pair.create(createMediaControllers(
new String[] {testPkgName1, testPkgName2},
new int[] {testUid1, testUid2}), 0L)), null,
@@ -1212,7 +1222,7 @@ public final class BackgroundRestrictionTest {
// Long-running FGS with 2 media sessions start/stop interlaced.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE,
List.of(Pair.create(createMediaControllers(
new String[] {testPkgName1, testPkgName2},
new int[] {testUid1, testUid2}), thresholdMs),
@@ -1230,17 +1240,17 @@ public final class BackgroundRestrictionTest {
// Long-running FGS with top state for a very short time.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null, null, List.of(0L),
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE, null, List.of(0L),
timeout(windowMs * 2).times(2));
// Long-running FGS with top state for extended time.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null, null, List.of(0L, windowMs * 2, 0L),
- timeout(windowMs * 2).times(0));
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE, null,
+ List.of(0L, windowMs * 2, 0L), timeout(windowMs * 2).times(0));
// Long-running FGS with top state, on and off frequently.
runTestLongFGSExemptionOnce(testPkgName1, testUid1, testPid1,
- FOREGROUND_SERVICE_TYPE_NONE, 0, null, null,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, null, OP_NONE, null,
List.of(0L, thresholdMs / 10, thresholdMs / 10, thresholdMs / 10,
thresholdMs / 10, thresholdMs / 10, thresholdMs / 10),
timeout(windowMs * 2).times(2));
@@ -1259,19 +1269,19 @@ public final class BackgroundRestrictionTest {
}
private void runTestLongFGSExemptionOnce(String packageName, int uid, int pid,
- int serviceType, long sleepMs, String perm,
+ int serviceType, long sleepMs, String perm, int op,
List<Pair<List<MediaController>, Long>> mediaControllers, List<Long> topStateChanges,
VerificationMode mode) throws Exception {
runExemptionTestOnce(
- packageName, uid, pid, serviceType, sleepMs, true, false, perm, mediaControllers,
- topStateChanges, true, true,
+ packageName, uid, pid, serviceType, sleepMs, true, false, perm, op,
+ mediaControllers, topStateChanges, true, true,
() -> checkNotificationShown(new String[] {packageName}, mode, false)
);
}
private void runExemptionTestOnce(String packageName, int uid, int pid,
int serviceType, long sleepMs, boolean stopAfterSleep,
- boolean withNotification, String perm,
+ boolean withNotification, String perm, int op,
List<Pair<List<MediaController>, Long>> mediaControllers,
List<Long> topStateChanges, boolean resetFGSTracker, boolean resetController,
RunnableWithException r) throws Exception {
@@ -1331,12 +1341,10 @@ public final class BackgroundRestrictionTest {
}
}
if (perm != null) {
- doReturn(PERMISSION_GRANTED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, perm, UserHandle.getUserId(uid));
- doReturn(PERMISSION_GRANTED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, perm);
+ setPermissionState(packageName, uid, perm, true);
+ if (op != OP_NONE) {
+ setAppOpState(packageName, uid, op, true);
+ }
mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
@@ -1357,12 +1365,10 @@ public final class BackgroundRestrictionTest {
mAppFGSTracker.onForegroundServiceStateChanged(packageName, uid, pid, false);
if (perm != null) {
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, perm, UserHandle.getUserId(uid));
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, perm);
+ setPermissionState(packageName, uid, perm, false);
+ if (op != OP_NONE) {
+ setAppOpState(packageName, uid, op, false);
+ }
mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
if (topStateThread != null) {
@@ -1575,7 +1581,7 @@ public final class BackgroundRestrictionTest {
// goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1584,7 +1590,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
@@ -1613,7 +1619,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1622,7 +1628,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1636,8 +1642,9 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
- null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
- new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
+ null, OP_NONE,
+ List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
+ new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1647,8 +1654,9 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
- null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
- new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
+ null, OP_NONE,
+ List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
+ new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1664,8 +1672,9 @@ public final class BackgroundRestrictionTest {
// but it ran on the top when the location service is active.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
- null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
- new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
+ null, OP_NONE,
+ List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
+ new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
List.of(0L, timeout * 2), listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1680,7 +1689,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1689,7 +1698,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1704,7 +1713,7 @@ public final class BackgroundRestrictionTest {
// but it ran on the top when the location service is active.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
- null, null, List.of(0L, timeout * 2), listener, stats, uids,
+ null, OP_NONE, null, List.of(0L, timeout * 2), listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1721,7 +1730,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with moderate current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
+ ACCESS_BACKGROUND_LOCATION, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1730,7 +1739,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with a bit higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
+ ACCESS_BACKGROUND_LOCATION, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1747,7 +1756,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
+ ACCESS_BACKGROUND_LOCATION, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true , RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1756,7 +1765,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
+ ACCESS_BACKGROUND_LOCATION, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1778,7 +1787,7 @@ public final class BackgroundRestrictionTest {
// goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1787,7 +1796,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
@@ -1819,7 +1828,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1830,7 +1839,7 @@ public final class BackgroundRestrictionTest {
// playback.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
- false, null, null, null, listener, stats, uids,
+ false, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1846,7 +1855,7 @@ public final class BackgroundRestrictionTest {
// Run with coarse location permission, with high current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_COARSE_LOCATION, null, null, listener, stats, uids,
+ ACCESS_COARSE_LOCATION, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1860,7 +1869,7 @@ public final class BackgroundRestrictionTest {
// Run with fine location permission, with high current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
- ACCESS_FINE_LOCATION, null, null, listener, stats, uids,
+ ACCESS_FINE_LOCATION, OP_FINE_LOCATION, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1874,8 +1883,9 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
- null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
- new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
+ null, OP_NONE,
+ List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
+ new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1887,8 +1897,9 @@ public final class BackgroundRestrictionTest {
// session.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
- null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
- new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
+ null, OP_NONE,
+ List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
+ new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1906,7 +1917,7 @@ public final class BackgroundRestrictionTest {
// goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, true, true,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1915,7 +1926,7 @@ public final class BackgroundRestrictionTest {
// Run with a service with notification for extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false,
- true, null, null, null, listener, stats, uids,
+ true, null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
@@ -1949,7 +1960,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1959,7 +1970,7 @@ public final class BackgroundRestrictionTest {
// it still should stay in the current restriction level as we exempt the location.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
- null, null, null, listener, stats, uids,
+ null, OP_NONE, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1984,7 +1995,7 @@ public final class BackgroundRestrictionTest {
private void runTestBgCurrentDrainExemptionOnce(String packageName, int uid, int pid,
int serviceType, long sleepMs, boolean stopAfterSleep, boolean withNotification,
- String perm, List<Pair<List<MediaController>, Long>> mediaControllers,
+ String perm, int op, List<Pair<List<MediaController>, Long>> mediaControllers,
List<Long> topStateChanges, TestAppRestrictionLevelListener listener,
BatteryUsageStats stats, int[] uids, double[] bg, double[] fgs, double[] fg,
double[] cached, boolean expectingTimeout, int expectingLevel, long timeout,
@@ -2001,18 +2012,16 @@ public final class BackgroundRestrictionTest {
mAppBatteryPolicy.reset();
}
if (perm != null) {
- doReturn(PERMISSION_GRANTED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, perm, UserHandle.getUserId(uid));
- doReturn(PERMISSION_GRANTED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, perm);
+ setPermissionState(packageName, uid, perm, true);
+ if (op != OP_NONE) {
+ setAppOpState(packageName, uid, op, true);
+ }
mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
waitForIdleHandler(mBgRestrictionController.getBackgroundHandler());
runExemptionTestOnce(
packageName, uid, pid, serviceType, sleepMs, stopAfterSleep, withNotification,
- perm, mediaControllers, topStateChanges, resetFGSTracker, false,
+ perm, op, mediaControllers, topStateChanges, resetFGSTracker, false,
() -> {
clearInvocations(mInjector.getAppStandbyInternal());
clearInvocations(mBgRestrictionController);
@@ -2060,16 +2069,36 @@ public final class BackgroundRestrictionTest {
}
);
if (perm != null) {
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, perm, UserHandle.getUserId(uid));
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, perm);
+ setPermissionState(packageName, uid, perm, false);
+ if (op != OP_NONE) {
+ setAppOpState(packageName, uid, op, false);
+ }
mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
}
+ private void setPermissionState(String packageName, int uid, String perm, boolean granted) {
+ doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, perm);
+ doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+ }
+
+ private void setAppOpState(String packageName, int uid, int op, boolean granted) {
+ try {
+ doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
+ .when(mAppOpsManager)
+ .checkOpNoThrow(op, uid, packageName);
+ doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
+ .when(mIAppOpsService)
+ .checkOperation(op, uid, packageName);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
@Test
public void testExcessiveBroadcasts() throws Exception {
final long windowMs = 5_000;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index a6194df18217..449177ef9b7d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -100,7 +100,7 @@
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
<uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" />
<uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" />
-
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
<queries>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 46515468489d..edacc165016f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -16,8 +16,6 @@
package com.android.server.accessibility;
-import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
-
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static junit.framework.Assert.assertFalse;
@@ -45,14 +43,10 @@ import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.InstallSourceInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -63,7 +57,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.R;
-import com.android.server.LocalServices;
import org.junit.Before;
import org.junit.Rule;
@@ -150,22 +143,12 @@ public class AccessibilitySecurityPolicyTest {
@Mock
private AccessibilityServiceInfo mMockA11yServiceInfo;
@Mock
- private ResolveInfo mMockResolveInfo;
- @Mock
- private ServiceInfo mMockServiceInfo;
- @Mock
- private ApplicationInfo mMockApplicationInfo;
- @Mock
- private ApplicationInfo mMockSourceApplicationInfo;
- @Mock
- private PackageInfo mMockSourcePackageInfo;
- @Mock
private PolicyWarningUIController mPolicyWarningUIController;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Before
- public void setUp() throws PackageManager.NameNotFoundException {
+ public void setUp() {
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mMockPackageManager);
mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
@@ -612,8 +595,7 @@ public class AccessibilitySecurityPolicyTest {
}
@Test
- public void onBoundServicesChanged_nonA11yTool_invokeAction()
- throws PackageManager.NameNotFoundException {
+ public void onBoundServicesChanged_nonA11yTool_invokeAction() {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
boundServices.add(mMockA11yServiceConnection);
initServiceInfoAndConnection(TEST_COMPONENT_NAME,
@@ -630,40 +612,11 @@ public class AccessibilitySecurityPolicyTest {
}
@Test
- public void onBoundServicesChanged_sysA11yTool_noAction()
- throws PackageManager.NameNotFoundException {
- final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
- initServiceInfoAndConnection(TEST_COMPONENT_NAME,
- mMockA11yServiceConnection,
- /* isAccessibilityTool= */ true,
- /* isSystemApp= */true,
- /* installSourceInfo= */ null);
- boundServices.add(mMockA11yServiceConnection);
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
- any());
- }
-
- @Test
- public void onBoundServicesChanged_nonSysA11yToolFromAllowedInstallerInAllowedList_noAction()
- throws PackageManager.NameNotFoundException {
+ public void onBoundServicesChanged_isA11yTool_noAction() {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
- final String allowedSourcePackageName = "com.allowed.install.package";
- mContext.getOrCreateTestableResources().addOverride(R.array
- .config_accessibility_allowed_install_source,
- new String[]{allowedSourcePackageName});
- // The allowed Installer should be system app in the allowed list.
- InstallSourceInfo allowedSource = initInstallSourceInfo(
- allowedSourcePackageName, /* isSystemApp= */ true);
initServiceInfoAndConnection(TEST_COMPONENT_NAME,
mMockA11yServiceConnection,
- /* isAccessibilityTool= */ true,
- /* isSystemApp= */ false,
- allowedSource);
+ /* isAccessibilityTool= */ true);
boundServices.add(mMockA11yServiceConnection);
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
@@ -675,37 +628,7 @@ public class AccessibilitySecurityPolicyTest {
}
@Test
- public void onBoundServicesChanged_nonSysA11yToolFromValidInstallerWithoutAllowedList_noAction()
- throws PackageManager.NameNotFoundException {
- final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
- final String validInstallerPackageName = "com.valid.install.package";
- final String defaultInstallerPackageName = "com.default.install.package";
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
- when(mPackageManagerInternal.getKnownPackageNames(PACKAGE_INSTALLER,
- TEST_USER_ID)).thenReturn(new String[]{defaultInstallerPackageName});
- mContext.getOrCreateTestableResources().addOverride(R.array
- .config_accessibility_allowed_install_source,
- new String[]{});
- // The valid Installer should be system app and not the default installer.
- InstallSourceInfo validSource = initInstallSourceInfo(
- validInstallerPackageName, /* isSystemApp= */ true);
- initServiceInfoAndConnection(TEST_COMPONENT_NAME,
- mMockA11yServiceConnection, /* isAccessibilityTool= */ true,
- /* isSystemApp= */ false,
- validSource);
- boundServices.add(mMockA11yServiceConnection);
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
- any());
- }
-
- @Test
- public void onSwitchUser_oldUserHadAction_invokeActionForOldUser()
- throws PackageManager.NameNotFoundException {
+ public void onSwitchUser_oldUserHadAction_invokeActionForOldUser() {
final int newUserId = 2;
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
initServiceInfoAndConnection(TEST_COMPONENT_NAME,
@@ -725,35 +648,9 @@ public class AccessibilitySecurityPolicyTest {
private void initServiceInfoAndConnection(ComponentName componentName,
AccessibilityServiceConnection connection,
- boolean isAccessibilityTool) throws PackageManager.NameNotFoundException {
- initServiceInfoAndConnection(componentName, connection, isAccessibilityTool, false, null);
- }
-
- private void initServiceInfoAndConnection(ComponentName componentName,
- AccessibilityServiceConnection connection,
- boolean isAccessibilityTool, boolean isSystemApp, InstallSourceInfo installSourceInfo)
- throws PackageManager.NameNotFoundException {
+ boolean isAccessibilityTool) {
when(connection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(componentName);
when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(isAccessibilityTool);
- when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
- mMockResolveInfo.serviceInfo = mMockServiceInfo;
- mMockServiceInfo.applicationInfo = mMockApplicationInfo;
- mMockServiceInfo.packageName = componentName.getPackageName();
- when(mMockApplicationInfo.isSystemApp()).thenReturn(isSystemApp);
- when(mMockPackageManager.getInstallSourceInfo(componentName.getPackageName())).thenReturn(
- installSourceInfo);
- }
-
- private InstallSourceInfo initInstallSourceInfo(String packageName, boolean isSystemApp)
- throws PackageManager.NameNotFoundException {
- final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- packageName, new SigningInfo(), null,
- packageName, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED);
- when(mMockPackageManager.getPackageInfo(packageName, 0)).thenReturn(
- mMockSourcePackageInfo);
- mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
- when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(isSystemApp);
- return installSourceInfo;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
index b8535c22451f..3cd967db3d91 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
@@ -116,8 +116,7 @@ public class PolicyWarningUIControllerTest {
mMockResolveInfo.serviceInfo = mMockServiceInfo;
when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
- when(mAccessibilitySecurityPolicy.isA11yCategoryService(
- mMockA11yServiceInfo)).thenReturn(false);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
mFakeNotificationController.onReceive(mContext,
PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index c0ba3c72beb4..5b6aebccbc11 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -45,6 +45,7 @@ public class AudioServiceTest {
private Context mContext;
private AudioSystemAdapter mAudioSystem;
@Spy private SystemServerAdapter mSpySystemServer;
+ private SettingsAdapter mSettingsAdapter;
// the class being unit-tested here
private AudioService mAudioService;
@@ -59,7 +60,9 @@ public class AudioServiceTest {
mContext = InstrumentationRegistry.getTargetContext();
mAudioSystem = new NoOpAudioSystemAdapter();
mSpySystemServer = spy(new NoOpSystemServerAdapter());
- mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer);
+ mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
+ mSettingsAdapter);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java
new file mode 100644
index 000000000000..bf80f27b432c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java
@@ -0,0 +1,102 @@
+/*
+ * 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.server.audio;
+
+import android.content.ContentResolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class NoOpSettingsAdapter extends SettingsAdapter {
+
+ /**
+ * No-op methods for Settings.Global
+ */
+
+ private Map<String, Integer> mGlobalIntSettings = new HashMap<>();
+ private Map<String, String> mGlobalStringSettings = new HashMap<>();
+
+ @Override
+ public int getGlobalInt(ContentResolver cr, String name, int def) {
+ return mGlobalIntSettings.getOrDefault(name, def);
+ }
+
+ @Override
+ public String getGlobalString(ContentResolver resolver, String name) {
+ return mGlobalStringSettings.getOrDefault(name, null);
+ }
+
+ @Override
+ public boolean putGlobalInt(ContentResolver cr, String name, int value) {
+ mGlobalIntSettings.put(name, value);
+ return true;
+ }
+
+ @Override
+ public boolean putGlobalString(ContentResolver resolver, String name, String value) {
+ mGlobalStringSettings.put(name, value);
+ return true;
+ }
+
+ /**
+ * No-op methods for Settings.System
+ */
+
+ private Map<String, Integer> mSystemIntSettings = new HashMap<>();
+
+ @Override
+ public int getSystemIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ return mSystemIntSettings.getOrDefault(name, def);
+ }
+
+ @Override
+ public boolean putSystemIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+ mSystemIntSettings.put(name, value);
+ return true;
+ }
+
+ /**
+ * No-op methods for Settings.Secure
+ */
+
+ private Map<String, Integer> mSecureIntSettings = new HashMap<>();
+ private Map<String, String> mSecureStringSettings = new HashMap<>();
+
+ @Override
+ public int getSecureIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+ return mSecureIntSettings.getOrDefault(name, def);
+ }
+
+ @Override
+ public String getSecureStringForUser(ContentResolver resolver, String name, int userHandle) {
+ return mSecureStringSettings.getOrDefault(name, null);
+ }
+
+ @Override
+ public boolean putSecureIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+ mSecureIntSettings.put(name, value);
+ return true;
+ }
+
+ @Override
+ public boolean putSecureStringForUser(ContentResolver cr, String name, String value,
+ int userHandle) {
+ mSecureStringSettings.put(name, value);
+ return true;
+ }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 61d7ede98f45..ef9f90fcd667 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -530,5 +530,10 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
return true;
}
+
+ @Override
+ public Context createContextAsUser(UserHandle user) {
+ return context;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 89bd10fdcdc3..484dc8450c57 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -158,7 +158,6 @@ import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.collections.Sets;
@@ -187,7 +186,6 @@ import java.util.concurrent.TimeUnit;
*/
@SmallTest
@Presubmit
-@Ignore("b/225415867")
public class DevicePolicyManagerTest extends DpmTestBase {
private static final String TAG = DevicePolicyManagerTest.class.getSimpleName();
@@ -1786,10 +1784,9 @@ public class DevicePolicyManagerTest extends DpmTestBase {
final int userId = CALLER_USER_HANDLE;
final UserHandle user = UserHandle.of(userId);
- mContext.applicationInfo = new ApplicationInfo();
- mContext.callerPermissions.add(permission.MANAGE_USERS);
- mContext.packageName = "com.android.frameworks.servicestests";
- getServices().addPackageContext(user, mContext);
+ mServiceContext.packageName = mRealTestContext.getPackageName();
+ mServiceContext.applicationInfo = new ApplicationInfo();
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
StringParceledListSlice oneCert = asSlice(new String[] {"1"});
@@ -6281,6 +6278,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
mServiceContext.packageName = mRealTestContext.getPackageName();
+ mServiceContext.applicationInfo = new ApplicationInfo();
mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setDeviceOwner();
@@ -6291,6 +6289,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception {
mServiceContext.packageName = mRealTestContext.getPackageName();
+ mServiceContext.applicationInfo = new ApplicationInfo();
mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_UID;
setAsProfileOwner(admin1);
@@ -6302,6 +6301,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Test
public void testGetOwnerInstalledCaCertsForDelegate() throws Exception {
mServiceContext.packageName = mRealTestContext.getPackageName();
+ mServiceContext.applicationInfo = new ApplicationInfo();
mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_UID;
setAsProfileOwner(admin1);
diff --git a/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
new file mode 100644
index 000000000000..150e3c60b91e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/PendingJobQueueTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.platform.test.annotations.LargeTest;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
+
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class PendingJobQueueTest {
+ private static final String TAG = PendingJobQueueTest.class.getSimpleName();
+
+ private static final int[] sRegJobPriorities = {
+ JobInfo.PRIORITY_HIGH, JobInfo.PRIORITY_DEFAULT,
+ JobInfo.PRIORITY_LOW, JobInfo.PRIORITY_MIN
+ };
+
+ private static JobInfo.Builder createJobInfo(int jobId) {
+ return new JobInfo.Builder(jobId, new ComponentName("foo", "bar"));
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder,
+ int callingUid) {
+ return JobStatus.createFromJobInfo(
+ jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag);
+ }
+
+ @Test
+ public void testAdd() {
+ List<JobStatus> jobs = new ArrayList<>();
+ jobs.add(createJobStatus("testAdd", createJobInfo(1), 1));
+ jobs.add(createJobStatus("testAdd", createJobInfo(2), 2));
+ jobs.add(createJobStatus("testAdd", createJobInfo(3).setExpedited(true), 3));
+ jobs.add(createJobStatus("testAdd", createJobInfo(4), 4));
+ jobs.add(createJobStatus("testAdd", createJobInfo(5).setExpedited(true), 5));
+
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ for (int i = 0; i < jobs.size(); ++i) {
+ jobQueue.add(jobs.get(i));
+ assertEquals(i + 1, jobQueue.size());
+ }
+
+ JobStatus job;
+ while ((job = jobQueue.next()) != null) {
+ jobs.remove(job);
+ }
+ assertEquals(0, jobs.size());
+ }
+
+ @Test
+ public void testAddAll() {
+ List<JobStatus> jobs = new ArrayList<>();
+ jobs.add(createJobStatus("testAddAll", createJobInfo(1), 1));
+ jobs.add(createJobStatus("testAddAll", createJobInfo(2), 2));
+ jobs.add(createJobStatus("testAddAll", createJobInfo(3).setExpedited(true), 3));
+ jobs.add(createJobStatus("testAddAll", createJobInfo(4), 4));
+ jobs.add(createJobStatus("testAddAll", createJobInfo(5).setExpedited(true), 5));
+
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ jobQueue.addAll(jobs);
+ assertEquals(jobs.size(), jobQueue.size());
+
+ JobStatus job;
+ while ((job = jobQueue.next()) != null) {
+ jobs.remove(job);
+ }
+ assertEquals(0, jobs.size());
+ }
+
+ @Test
+ public void testClear() {
+ List<JobStatus> jobs = new ArrayList<>();
+ jobs.add(createJobStatus("testClear", createJobInfo(1), 1));
+ jobs.add(createJobStatus("testClear", createJobInfo(2), 2));
+ jobs.add(createJobStatus("testClear", createJobInfo(3).setExpedited(true), 3));
+ jobs.add(createJobStatus("testClear", createJobInfo(4), 4));
+ jobs.add(createJobStatus("testClear", createJobInfo(5).setExpedited(true), 5));
+
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ jobQueue.addAll(jobs);
+ assertEquals(jobs.size(), jobQueue.size());
+ assertNotNull(jobQueue.next());
+
+ jobQueue.clear();
+ assertEquals(0, jobQueue.size());
+ assertNull(jobQueue.next());
+ }
+
+ @Test
+ public void testRemove() {
+ List<JobStatus> jobs = new ArrayList<>();
+ jobs.add(createJobStatus("testRemove", createJobInfo(1), 1));
+ jobs.add(createJobStatus("testRemove", createJobInfo(2), 2));
+ jobs.add(createJobStatus("testRemove", createJobInfo(3).setExpedited(true), 3));
+ jobs.add(createJobStatus("testRemove", createJobInfo(4), 4));
+ jobs.add(createJobStatus("testRemove", createJobInfo(5).setExpedited(true), 5));
+
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ jobQueue.addAll(jobs);
+
+ for (int i = 0; i < jobs.size(); ++i) {
+ jobQueue.remove(jobs.get(i));
+ assertEquals(jobs.size() - i - 1, jobQueue.size());
+ }
+ assertNull(jobQueue.next());
+ }
+
+ @Test
+ public void testPendingJobSorting() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+
+ // First letter in job variable name indicate regular (r) or expedited (e).
+ // Capital letters in job variable name indicate the app/UID.
+ // Numbers in job variable name indicate the enqueue time.
+ // Expected sort order:
+ // eA7 > rA1 > eB6 > rB2 > eC3 > rD4 > eE5 > eF9 > rF8 > eC11 > rC10 > rG12 > rG13 > eE14
+ // Intentions:
+ // * A jobs let us test skipping both regular and expedited jobs of other apps
+ // * B jobs let us test skipping only regular job of another app without going too far
+ // * C jobs test that regular jobs don't skip over other app's jobs and that EJs only
+ // skip up to level of the earliest regular job
+ // * E jobs test that expedited jobs don't skip the line when the app has no regular jobs
+ // * F jobs test correct expedited/regular ordering doesn't push jobs too high in list
+ // * G jobs test correct ordering for regular jobs
+ // * H job tests correct behavior when enqueue times are the same
+ JobStatus rA1 = createJobStatus("testPendingJobSorting", createJobInfo(1), 1);
+ JobStatus rB2 = createJobStatus("testPendingJobSorting", createJobInfo(2), 2);
+ JobStatus eC3 = createJobStatus("testPendingJobSorting",
+ createJobInfo(3).setExpedited(true), 3);
+ JobStatus rD4 = createJobStatus("testPendingJobSorting", createJobInfo(4), 4);
+ JobStatus eE5 = createJobStatus("testPendingJobSorting",
+ createJobInfo(5).setExpedited(true), 5);
+ JobStatus eB6 = createJobStatus("testPendingJobSorting",
+ createJobInfo(6).setExpedited(true), 2);
+ JobStatus eA7 = createJobStatus("testPendingJobSorting",
+ createJobInfo(7).setExpedited(true), 1);
+ JobStatus rH8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 8);
+ JobStatus rF8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 6);
+ JobStatus eF9 = createJobStatus("testPendingJobSorting",
+ createJobInfo(9).setExpedited(true), 6);
+ JobStatus rC10 = createJobStatus("testPendingJobSorting", createJobInfo(10), 3);
+ JobStatus eC11 = createJobStatus("testPendingJobSorting",
+ createJobInfo(11).setExpedited(true), 3);
+ JobStatus rG12 = createJobStatus("testPendingJobSorting", createJobInfo(12), 7);
+ JobStatus rG13 = createJobStatus("testPendingJobSorting", createJobInfo(13), 7);
+ JobStatus eE14 = createJobStatus("testPendingJobSorting",
+ createJobInfo(14).setExpedited(true), 5);
+
+ rA1.enqueueTime = 10;
+ rB2.enqueueTime = 20;
+ eC3.enqueueTime = 30;
+ rD4.enqueueTime = 40;
+ eE5.enqueueTime = 50;
+ eB6.enqueueTime = 60;
+ eA7.enqueueTime = 70;
+ rF8.enqueueTime = 80;
+ rH8.enqueueTime = 80;
+ eF9.enqueueTime = 90;
+ rC10.enqueueTime = 100;
+ eC11.enqueueTime = 110;
+ rG12.enqueueTime = 120;
+ rG13.enqueueTime = 130;
+ eE14.enqueueTime = 140;
+
+ // Add in random order so sorting is apparent.
+ jobQueue.add(eC3);
+ jobQueue.add(eE5);
+ jobQueue.add(rA1);
+ jobQueue.add(rG13);
+ jobQueue.add(rD4);
+ jobQueue.add(eA7);
+ jobQueue.add(rG12);
+ jobQueue.add(rH8);
+ jobQueue.add(rF8);
+ jobQueue.add(eB6);
+ jobQueue.add(eE14);
+ jobQueue.add(eF9);
+ jobQueue.add(rB2);
+ jobQueue.add(rC10);
+ jobQueue.add(eC11);
+
+ checkPendingJobInvariants(jobQueue);
+ final JobStatus[] expectedOrder = new JobStatus[]{
+ eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rH8, rF8, eC11, rC10, rG12, rG13, eE14};
+ int idx = 0;
+ JobStatus job;
+ while ((job = jobQueue.next()) != null) {
+ assertEquals("List wasn't correctly sorted @ index " + idx,
+ expectedOrder[idx].getJobId(), job.getJobId());
+ idx++;
+ }
+ }
+
+ @Test
+ public void testPendingJobSorting_Random() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ Random random = new Random(1); // Always use the same series of pseudo random values.
+
+ for (int i = 0; i < 5000; ++i) {
+ JobStatus job = createJobStatus("testPendingJobSorting_Random",
+ createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
+ job.enqueueTime = random.nextInt(1_000_000);
+ jobQueue.add(job);
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+
+ @Test
+ public void testPendingJobSortingTransitivity() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
+ Random random = new Random(seed);
+
+ jobQueue.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
+ createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
+ job.enqueueTime = random.nextInt(1_000_000);
+ job.overrideState = random.nextInt(4);
+ jobQueue.add(job);
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+ }
+
+ @Test
+ @LargeTest
+ public void testPendingJobSortingTransitivity_Concentrated() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
+ Random random = new Random(seed);
+
+ jobQueue.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ JobStatus job = createJobStatus("testPendingJobSortingTransitivity_Concentrated",
+ createJobInfo(i).setExpedited(random.nextFloat() < .03),
+ random.nextInt(20));
+ job.enqueueTime = random.nextInt(250);
+ job.overrideState = random.nextFloat() < .01
+ ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
+ jobQueue.add(job);
+ Log.d(TAG, testJobToString(job));
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+ }
+
+ @Test
+ public void testPendingJobSorting_Random_WithPriority() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ Random random = new Random(1); // Always use the same series of pseudo random values.
+
+ for (int i = 0; i < 5000; ++i) {
+ final boolean isEj = random.nextBoolean();
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus("testPendingJobSorting_Random_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(250));
+ job.enqueueTime = random.nextInt(1_000_000);
+ jobQueue.add(job);
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+
+ @Test
+ public void testPendingJobSortingTransitivity_WithPriority() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
+ Random random = new Random(seed);
+
+ jobQueue.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ final boolean isEj = random.nextBoolean();
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus("testPendingJobSortingTransitivity_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(50));
+ job.enqueueTime = random.nextInt(1_000_000);
+ job.overrideState = random.nextInt(4);
+ jobQueue.add(job);
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+ }
+
+ @Test
+ @LargeTest
+ public void testPendingJobSortingTransitivity_Concentrated_WithPriority() {
+ PendingJobQueue jobQueue = new PendingJobQueue();
+ // Always use the same series of pseudo random values.
+ for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
+ Random random = new Random(seed);
+
+ jobQueue.clear();
+
+ for (int i = 0; i < 300; ++i) {
+ final boolean isEj = random.nextFloat() < .03;
+ final int priority;
+ if (isEj) {
+ priority = random.nextBoolean() ? JobInfo.PRIORITY_MAX : JobInfo.PRIORITY_HIGH;
+ } else {
+ priority = sRegJobPriorities[random.nextInt(sRegJobPriorities.length)];
+ }
+ JobStatus job = createJobStatus(
+ "testPendingJobSortingTransitivity_Concentrated_WithPriority",
+ createJobInfo(i).setExpedited(isEj).setPriority(priority),
+ random.nextInt(20));
+ job.enqueueTime = random.nextInt(250);
+ job.overrideState = random.nextFloat() < .01
+ ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
+ jobQueue.add(job);
+ Log.d(TAG, testJobToString(job));
+ }
+
+ checkPendingJobInvariants(jobQueue);
+ }
+ }
+
+ private void checkPendingJobInvariants(PendingJobQueue jobQueue) {
+ final SparseBooleanArray regJobSeen = new SparseBooleanArray();
+ final SparseIntArray lastOverrideStateSeen = new SparseIntArray();
+ // Latest priority enqueue times seen for each priority for each app.
+ final SparseArray<SparseLongArray> latestPriorityRegEnqueueTimesPerUid =
+ new SparseArray<>();
+ final SparseArray<SparseLongArray> latestPriorityEjEnqueueTimesPerUid = new SparseArray<>();
+ final int noEntry = -1;
+
+ JobStatus job;
+ jobQueue.resetIterator();
+ while ((job = jobQueue.next()) != null) {
+ final int uid = job.getSourceUid();
+
+ // Invariant #1: All jobs (for a UID) are sorted by override state
+ // Invariant #2: All jobs (for a UID) are sorted by priority order
+ // Invariant #3: Jobs (for a UID) with the same priority are sorted by enqueue time.
+ // Invariant #4: EJs (for a UID) should be before regular jobs
+
+ final int prevOverrideState = lastOverrideStateSeen.get(uid, noEntry);
+ lastOverrideStateSeen.put(uid, job.overrideState);
+ if (prevOverrideState == noEntry) {
+ // First job for UID
+ continue;
+ }
+
+ // Invariant 1
+ if (prevOverrideState != job.overrideState) {
+ assertTrue(prevOverrideState > job.overrideState);
+ // Override state can make ordering weird. Clear the other cached states for this
+ // UID to avoid confusion in the other checks.
+ latestPriorityEjEnqueueTimesPerUid.remove(uid);
+ latestPriorityRegEnqueueTimesPerUid.remove(uid);
+ regJobSeen.delete(uid);
+ }
+
+ final int priority = job.getEffectivePriority();
+ final SparseArray<SparseLongArray> latestPriorityEnqueueTimesPerUid =
+ job.isRequestedExpeditedJob()
+ ? latestPriorityEjEnqueueTimesPerUid
+ : latestPriorityRegEnqueueTimesPerUid;
+ SparseLongArray latestPriorityEnqueueTimes = latestPriorityEnqueueTimesPerUid.get(uid);
+ if (latestPriorityEnqueueTimes != null) {
+ // Invariant 2
+ for (int p = priority - 1; p >= JobInfo.PRIORITY_MIN; --p) {
+ // If we haven't seen the priority, there shouldn't be an entry in the array.
+ assertEquals("Jobs not properly sorted by priority for uid " + uid,
+ noEntry, latestPriorityEnqueueTimes.get(p, noEntry));
+ }
+
+ // Invariant 3
+ final long lastSeenPriorityEnqueueTime =
+ latestPriorityEnqueueTimes.get(priority, noEntry);
+ if (lastSeenPriorityEnqueueTime != noEntry) {
+ assertTrue("Jobs with same priority not sorted by enqueue time: "
+ + lastSeenPriorityEnqueueTime + " vs " + job.enqueueTime,
+ lastSeenPriorityEnqueueTime <= job.enqueueTime);
+ }
+ } else {
+ latestPriorityEnqueueTimes = new SparseLongArray();
+ latestPriorityEnqueueTimesPerUid.put(uid, latestPriorityEnqueueTimes);
+ }
+ latestPriorityEnqueueTimes.put(priority, job.enqueueTime);
+
+ // Invariant 4
+ if (!job.isRequestedExpeditedJob()) {
+ regJobSeen.put(uid, true);
+ } else if (regJobSeen.get(uid)) {
+ fail("UID " + uid + " had an EJ ordered after a regular job");
+ }
+ }
+ }
+
+ private static String testJobToString(JobStatus job) {
+ return "testJob " + job.getSourceUid() + "/" + job.getJobId()
+ + "/o" + job.overrideState
+ + "/p" + job.getEffectivePriority()
+ + "/b" + job.lastEvaluatedBias
+ + "/" + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index af8ac6f412f5..7634b09b4dc6 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -118,6 +118,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
@@ -166,6 +167,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
import com.android.internal.util.test.FsUtil;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.usage.AppStandbyInternal;
import com.google.common.util.concurrent.AbstractFuture;
@@ -216,6 +218,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -274,6 +277,7 @@ public class NetworkPolicyManagerServiceTest {
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
private ActivityManagerInternal mActivityManagerInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
@@ -335,6 +339,7 @@ public class NetworkPolicyManagerServiceTest {
when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class);
+ mPackageManagerInternal = addLocalServiceMock(PackageManagerInternal.class);
final PowerSaveState state = new PowerSaveState.Builder()
.setBatterySaverEnabled(false).build();
@@ -483,8 +488,15 @@ public class NetworkPolicyManagerServiceTest {
.thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B));
when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
.thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C));
- when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
- buildInstalledApplicationInfoList());
+ doAnswer(arg -> {
+ final Consumer<AndroidPackage> consumer =
+ (Consumer<AndroidPackage>) arg.getArguments()[0];
+ for (AndroidPackage androidPackage : buildInstalledPackageList()) {
+ consumer.accept(androidPackage);
+ }
+ return null;
+ }).when(mPackageManagerInternal).forEachInstalledPackage(
+ any(Consumer.class), anyInt());
when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
@@ -536,6 +548,7 @@ public class NetworkPolicyManagerServiceTest {
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.removeServiceForTest(AppStandbyInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@After
@@ -2037,14 +2050,20 @@ public class NetworkPolicyManagerServiceTest {
return ai;
}
- private List<ApplicationInfo> buildInstalledApplicationInfoList() {
- final List<ApplicationInfo> installedApps = new ArrayList<>();
- installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A));
- installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B));
- installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C));
+ private List<AndroidPackage> buildInstalledPackageList() {
+ final List<AndroidPackage> installedApps = new ArrayList<>();
+ installedApps.add(createPackageMock(UID_A));
+ installedApps.add(createPackageMock(UID_B));
+ installedApps.add(createPackageMock(UID_C));
return installedApps;
}
+ private AndroidPackage createPackageMock(int uid) {
+ final AndroidPackage androidPackage = mock(AndroidPackage.class);
+ when(androidPackage.getUid()).thenReturn(uid);
+ return androidPackage;
+ }
+
private List<UserInfo> buildUserInfoList() {
final List<UserInfo> users = new ArrayList<>();
users.add(new UserInfo(USER_ID, "user1", 0));
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 0301e94a6acc..b907c62be6fb 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -277,6 +277,6 @@ public class VibrationScalerTest {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
- mVibrationSettings.updateSettings();
+ mVibrationSettings.update();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index ec16188bfc1d..0c28d8c761ab 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -476,6 +476,24 @@ public class VibrationSettingsTest {
}
@Test
+ public void shouldIgnoreVibration_updateTriggeredAfterInternalRingerModeChanged() {
+ // Vibrating settings on are overruled by ringer mode.
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+
+ assertVibrationNotIgnoredForUsage(USAGE_RINGTONE);
+
+ // Testing the broadcast flow manually.
+ mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+
+ assertVibrationIgnoredForUsage(USAGE_RINGTONE, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+ }
+
+ @Test
public void shouldVibrateInputDevices_returnsSettingsValue() {
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
assertTrue(mVibrationSettings.shouldVibrateInputDevices());
@@ -577,7 +595,7 @@ public class VibrationSettingsTest {
Settings.System.putIntForUser(mContextSpy.getContentResolver(),
Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
UserHandle.USER_CURRENT);
- mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(Intent.ACTION_USER_SWITCHED));
assertEquals(VIBRATION_INTENSITY_LOW,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
@@ -587,7 +605,7 @@ public class VibrationSettingsTest {
public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- mVibrationSettings.updateSettings();
+ mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is off, fallback to default value.
assertEquals(VIBRATION_INTENSITY_MEDIUM,
@@ -596,7 +614,7 @@ public class VibrationSettingsTest {
mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- mVibrationSettings.updateSettings();
+ mVibrationSettings.update();
assertEquals(VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
// If haptic feedback is on, fallback to that value.
@@ -633,7 +651,7 @@ public class VibrationSettingsTest {
mVibrationSettings.shouldIgnoreVibration(UID,
new VibrationAttributes.Builder()
.setUsage(usage)
- .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+ .setFlags(flags)
.build()));
}
@@ -654,18 +672,19 @@ public class VibrationSettingsTest {
Settings.System.putStringForUser(
mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
- mVibrationSettings.updateSettings();
+ mVibrationSettings.update();
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
- mVibrationSettings.updateSettings();
+ mVibrationSettings.update();
}
private void setRingerMode(int ringerMode) {
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
+ mVibrationSettings.update();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 704729ea22c4..9f135918daa2 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -257,13 +257,13 @@ public class VibrationThreadTest {
assertTrue(mThread.isRunningVibrationId(vibrationId));
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
waitForCompletion();
assertFalse(mThread.isRunningVibrationId(vibrationId));
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
@@ -288,10 +288,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(1000)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -310,10 +310,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(5550)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -334,10 +334,10 @@ public class VibrationThreadTest {
assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
1000 + TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(2, fakeVibrator.getEffectSegments(vibrationId).size());
// First time turn vibrator ON for minimum of 1s.
@@ -371,13 +371,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -397,13 +398,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -647,7 +649,7 @@ public class VibrationThreadTest {
waitForCompletion();
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
}
@Test
@@ -1043,7 +1045,8 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(cancellingThread).
Thread cancellingThread = new Thread(
- () -> conductor.notifyCancelled(/* immediate= */ false));
+ () -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false));
cancellingThread.start();
// Cancelling the vibration should be fast and return right away, even if the thread is
@@ -1052,7 +1055,7 @@ public class VibrationThreadTest {
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1080,13 +1083,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
- () -> conductor.notifyCancelled(/* immediate= */ false));
+ () -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1113,13 +1117,14 @@ public class VibrationThreadTest {
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
- new Thread(() -> conductor.notifyCancelled(/* immediate= */ false));
+ new Thread(() -> conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SCREEN_OFF, /* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1139,7 +1144,7 @@ public class VibrationThreadTest {
verify(mVibrationToken).linkToDeath(same(conductor), eq(0));
verify(mVibrationToken).unlinkToDeath(same(conductor), eq(0));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1193,12 +1198,13 @@ public class VibrationThreadTest {
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
// Will stop the ramp down right away.
- conductor.notifyCancelled(/* immediate= */ true);
+ conductor.notifyCancelled(
+ Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE, /* immediate= */ true);
waitForCompletion();
// Does not cancel already finished vibration, but releases vibrator.
verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
- eq(Vibration.Status.CANCELLED));
+ eq(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE));
verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
}
@@ -1214,10 +1220,10 @@ public class VibrationThreadTest {
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
- conductor.notifyCancelled(/* immediate= */ false);
+ conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
// Duration extended for 10000 + 15.
assertEquals(Arrays.asList(expectedOneShot(10_015)),
@@ -1337,7 +1343,7 @@ public class VibrationThreadTest {
VibrationStepConductor conductor2 = startThreadAndDispatcher(vibrationId2, effect2);
// Effect2 won't complete on its own. Cancel it after a couple of repeats.
Thread.sleep(150); // More than two TICKs.
- conductor2.notifyCancelled(/* immediate= */ false);
+ conductor2.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
waitForCompletion();
startThreadAndDispatcher(vibrationId3, effect3);
@@ -1346,7 +1352,7 @@ public class VibrationThreadTest {
// Effect4 is a long oneshot, but it gets cancelled as fast as possible.
long start4 = System.currentTimeMillis();
VibrationStepConductor conductor4 = startThreadAndDispatcher(vibrationId4, effect4);
- conductor4.notifyCancelled(/* immediate= */ true);
+ conductor4.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ true);
waitForCompletion();
long duration4 = System.currentTimeMillis() - start4;
@@ -1366,7 +1372,7 @@ public class VibrationThreadTest {
// Effect2: repeating, cancelled.
verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2);
- verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED_BY_USER);
// The exact count of segments might vary, so just check that there's more than 2 and
// all elements are the same segment.
@@ -1384,7 +1390,7 @@ public class VibrationThreadTest {
fakeVibrator.getEffectSegments(vibrationId3));
// Effect4: cancelled quickly.
- verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED);
+ verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_SUPERSEDED);
assertTrue("Tested duration=" + duration4, duration4 < 2000);
// Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 92736c517782..9c72ce22c857 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -673,6 +673,42 @@ public class VibratorManagerServiceTest {
}
@Test
+ public void vibrate_withVibrationAttributesEnforceFreshSettings_refreshesVibrationSettings()
+ throws Exception {
+ mockVibrators(0);
+ mVibratorProviders.get(0).setSupportedEffects(VibrationEffect.EFFECT_CLICK,
+ VibrationEffect.EFFECT_TICK);
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationAttributes enforceFreshAttrs = new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_NOTIFICATION)
+ .setFlags(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)
+ .build();
+
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), NOTIFICATION_ATTRS);
+ // VibrationThread will start this vibration async, so wait before vibrating a second time.
+ assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 0,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), enforceFreshAttrs);
+ // VibrationThread will start this vibration async, so wait before checking.
+ assertTrue(waitUntil(s -> mVibratorProviders.get(0).getAllEffectSegments().size() > 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ assertEquals(
+ Arrays.asList(
+ expectedPrebaked(VibrationEffect.EFFECT_CLICK,
+ VibrationEffect.EFFECT_STRENGTH_STRONG),
+ expectedPrebaked(VibrationEffect.EFFECT_TICK,
+ VibrationEffect.EFFECT_STRENGTH_LIGHT)),
+ mVibratorProviders.get(0).getAllEffectSegments());
+ }
+
+ @Test
public void vibrate_withAttributesUnknownUsage_usesEffectToIdentifyTouchUsage() {
VibratorManagerService service = createSystemReadyService();
@@ -1280,7 +1316,11 @@ public class VibratorManagerServiceTest {
}
private VibrationEffectSegment expectedPrebaked(int effectId) {
- return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ }
+
+ private VibrationEffectSegment expectedPrebaked(int effectId, int effectStrength) {
+ return new PrebakedSegment(effectId, false, effectStrength);
}
private void mockCapabilities(long... capabilities) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 40b460157bc2..91692676e216 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -414,6 +414,33 @@ public class DisplayContentTests extends WindowTestsBase {
imeContainer.setOrganizer(null);
}
+ @Test
+ public void testImeContainerIsReparentedUnderParentWhenOrganized() {
+ final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
+ "startingWin");
+ startingWin.setHasSurface(true);
+ assertTrue(startingWin.canBeImeTarget());
+
+ final Transaction transaction = mDisplayContent.getPendingTransaction();
+ spyOn(transaction);
+
+ // Organized the ime container.
+ final IDisplayAreaOrganizer mockImeOrganizer = mock(IDisplayAreaOrganizer.class);
+ when(mockImeOrganizer.asBinder()).thenReturn(new Binder());
+ imeContainer.setOrganizer(mockImeOrganizer);
+
+ // Verify that the ime container surface is reparented under
+ // its parent surface as a consequence of the setOrganizer call.
+ SurfaceControl imeParentSurfaceControl = imeContainer.getParentSurfaceControl();
+ verify(transaction).reparent(imeContainer.getSurfaceControl(), imeParentSurfaceControl);
+
+ // Clean up organizer.
+ imeContainer.setOrganizer(null);
+ }
+
/**
* This tests root task movement between displays and proper root task's, task's and app token's
* display container references updates.
@@ -1984,7 +2011,7 @@ public class DisplayContentTests extends WindowTestsBase {
appWin2.setHasSurface(true);
assertTrue(appWin2.canBeImeTarget());
doReturn(true).when(appWin1).isClosing();
- doReturn(true).when(appWin1).inAppOrRecentsTransition();
+ doReturn(true).when(appWin1).inTransitionSelfOrParent();
// Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will
// be attached and shown on the display at this time.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index b2f0b420d67c..ce861595535c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -190,7 +191,11 @@ public class SystemServicesTestRule implements TestRule {
private void setUpSystemCore() {
doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
doAnswer(invocation -> {
- mDeviceConfigListeners.add(invocation.getArgument(2));
+ // Exclude CONSTRAIN_DISPLAY_APIS because ActivityRecord#sConstrainDisplayApisConfig
+ // only registers once and it doesn't reference to outside.
+ if (!NAMESPACE_CONSTRAIN_DISPLAY_APIS.equals(invocation.getArgument(0))) {
+ mDeviceConfigListeners.add(invocation.getArgument(2));
+ }
// SizeCompatTests uses setNeverConstrainDisplayApisFlag, and ActivityRecordTests
// uses splash_screen_exception_list. So still execute real registration.
return invocation.callRealMethod();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index c6158662d110..b1c9d3de304b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
@@ -249,19 +250,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer.
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
- assertThrows(SecurityException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ assertApplyTransactionDisallowed(mTransaction);
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
}
@Test
@@ -272,19 +267,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer.
mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
- assertThrows(SecurityException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ assertApplyTransactionDisallowed(mTransaction);
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
}
@Test
@@ -298,27 +287,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer.
mTransaction.deleteTaskFragment(mFragmentWindowToken);
- assertThrows(SecurityException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ assertApplyTransactionDisallowed(mTransaction);
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
// No lifecycle update when the TaskFragment is not recorded.
verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities();
mAtm.mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@@ -335,13 +318,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer.
mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
- assertThrows(SecurityException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ assertApplyTransactionDisallowed(mTransaction);
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -350,7 +327,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
"Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@@ -423,20 +400,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer.
mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
- assertThrows(SecurityException.class, () -> {
- try {
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
- } catch (RemoteException e) {
- fail();
- }
- });
+ assertApplyTransactionDisallowed(mTransaction);
// Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
- mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@@ -454,6 +425,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mAtm.mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token);
+ doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -484,6 +456,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
}
+ @Test
+ public void testCanSendPendingTaskFragmentEventsAfterActivityResumed() {
+ // Task - TaskFragment - Activity.
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(mOrganizer)
+ .setFragmentToken(mFragmentToken)
+ .createActivityCount(1)
+ .build();
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+
+ // Mock the task to invisible
+ doReturn(false).when(task).shouldBeVisible(any());
+ taskFragment.setResumedActivity(null, "test");
+
+ // Sending events
+ mController.registerOrganizer(mIOrganizer);
+ taskFragment.mTaskFragmentAppearedSent = true;
+ mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
+ mController.dispatchPendingEvents();
+
+ // Verifies that event was not sent
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+ // Mock the task becomes visible, and activity resumed
+ doReturn(true).when(task).shouldBeVisible(any());
+ taskFragment.setResumedActivity(activity, "test");
+
+ // Verifies that event is sent.
+ mController.dispatchPendingEvents();
+ verify(mOrganizer).onTaskFragmentInfoChanged(any());
+ }
+
/**
* Tests that a task fragment info changed event is still sent if the task is invisible only
* when the info changed event is because of the last activity in a task finishing.
@@ -541,6 +547,66 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
/**
+ * For config change to untrusted embedded TaskFragment, we only allow bounds change within
+ * its parent bounds.
+ */
+ @Test
+ public void testUntrustedEmbedding_configChange() throws RemoteException {
+ mController.registerOrganizer(mIOrganizer);
+ mOrganizer.applyTransaction(mTransaction);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+ doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode();
+ final Task task = createTask(mDisplayContent);
+ final Rect taskBounds = new Rect(task.getBounds());
+ final Rect taskAppBounds = new Rect(task.getWindowConfiguration().getAppBounds());
+ final int taskScreenWidthDp = task.getConfiguration().screenWidthDp;
+ final int taskScreenHeightDp = task.getConfiguration().screenHeightDp;
+ final int taskSmallestScreenWidthDp = task.getConfiguration().smallestScreenWidthDp;
+ task.addChild(mTaskFragment, POSITION_TOP);
+
+ // Throw exception if the transaction is trying to change bounds of an untrusted outside of
+ // its parent's.
+
+ // setBounds
+ final Rect tfBounds = new Rect(taskBounds);
+ tfBounds.right++;
+ mTransaction.setBounds(mFragmentWindowToken, tfBounds);
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTransaction.setBounds(mFragmentWindowToken, taskBounds);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // setAppBounds
+ final Rect tfAppBounds = new Rect(taskAppBounds);
+ tfAppBounds.right++;
+ mTransaction.setAppBounds(mFragmentWindowToken, tfAppBounds);
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTransaction.setAppBounds(mFragmentWindowToken, taskAppBounds);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // setScreenSizeDp
+ mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp + 1,
+ taskScreenHeightDp + 1);
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp, taskScreenHeightDp);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // setSmallestScreenWidthDp
+ mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp + 1);
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Any of the change mask is not allowed.
+ mTransaction.setFocusable(mFragmentWindowToken, false);
+ assertApplyTransactionDisallowed(mTransaction);
+ }
+
+ /**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction} to apply the transaction,
*/
@@ -556,4 +622,24 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
wct.createTaskFragment(params);
}
+
+ /** Asserts that applying the given transaction will throw a {@link SecurityException}. */
+ private void assertApplyTransactionDisallowed(WindowContainerTransaction t) {
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(t);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+ }
+
+ /** Asserts that applying the given transaction will not throw any exception. */
+ private void assertApplyTransactionAllowed(WindowContainerTransaction t) {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(t);
+ } catch (RemoteException e) {
+ fail();
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7e5d017e2bc4..3330ca982068 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -617,8 +617,8 @@ public class TransitionTests extends WindowTestsBase {
}
player.startTransition();
- assertFalse(statusBar.mToken.inTransition());
- assertFalse(decorToken.inTransition());
+ assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
+ assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken));
assertTrue(ime.mToken.inTransition());
assertTrue(task.inTransition());
assertTrue(asyncRotationController.isTargetToken(decorToken));
@@ -735,7 +735,7 @@ public class TransitionTests extends WindowTestsBase {
statusBar.setOrientationChanging(true);
player.startTransition();
// Non-app windows should not be collected.
- assertFalse(statusBar.mToken.inTransition());
+ assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 08320f8c423f..9d4fe2783992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -70,7 +70,7 @@ import org.junit.runner.RunWith;
/**
* Build/Install/Run:
- * atest WmTests:WindowManagerServiceTests
+ * atest WmTests:WindowManagerServiceTests
*/
@SmallTest
@Presubmit
@@ -266,7 +266,8 @@ public class WindowManagerServiceTests extends WindowTestsBase {
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
@Override
- public void onAnimatorScaleChanged(float v) throws RemoteException {}
+ public void onAnimatorScaleChanged(float v) throws RemoteException {
+ }
});
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
TYPE_APPLICATION_ATTACHED_DIALOG);
@@ -292,7 +293,8 @@ public class WindowManagerServiceTests extends WindowTestsBase {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString());
- when(mWm.mAtmService.isInstrumenting(callingPid)).thenReturn(true);
+ when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
+ android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
mWm.setInTouchMode(!currentTouchMode);
@@ -306,7 +308,8 @@ public class WindowManagerServiceTests extends WindowTestsBase {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString());
- when(mWm.mAtmService.isInstrumenting(callingPid)).thenReturn(false);
+ when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
+ android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(false);
mWm.setInTouchMode(!currentTouchMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index b4d305bbe4d0..9faf499f391a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -309,6 +309,51 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testOrganizerDeathReturnsRegistrationToPrevious() throws RemoteException {
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final Task rootTask2 = createRootTask();
+ final Task task2 = createTask(rootTask2);
+ final Task rootTask3 = createRootTask();
+ final Task task3 = createTask(rootTask3);
+ final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+
+ // verify that tasks are returned and taskAppeared is not called
+ assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3);
+ verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
+ verify(organizer, times(0)).onTaskVanished(any());
+ assertTrue(rootTask.isOrganized());
+
+ // Now we replace the registration and verify the new organizer receives existing tasks
+ final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
+ final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3);
+ verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
+ verify(organizer2, times(0)).onTaskVanished(any());
+ // Removed tasks from the original organizer
+ assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+ assertTrue(rootTask2.isOrganized());
+
+ // Trigger binderDied for second one, the first one should automatically be reregistered
+ // so we verify that it's now seeing changes.
+ mWm.mAtmService.mTaskOrganizerController.getTaskOrganizerState(organizer2.asBinder())
+ .getDeathRecipient().binderDied();
+
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ verify(organizer, times(3))
+ .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+ }
+
+ @Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
final Task rootTask = createRootTask();
final Task task = createTask(rootTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index bd1f9d536c28..6e0d8549defc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1264,6 +1264,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID,
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
}
+ spyOn(taskFragment);
return taskFragment;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index eea3f844b40f..0f223ca037ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -293,8 +293,7 @@ public class ZOrderingTests extends WindowTestsBase {
public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() {
final WindowState appBelowImeTarget = createWindow("appBelowImeTarget");
final WindowState imeAppTarget = createWindow("imeAppTarget");
- final WindowState appAboveImeTarget = createWindow(imeAppTarget, TYPE_APPLICATION,
- "appAboveImeTarget");
+ final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
mDisplayContent.setImeLayeringTarget(imeAppTarget);
mDisplayContent.setImeControlTarget(imeAppTarget);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dda5ea7aab53..3691fb06abdb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9499,42 +9499,26 @@ public class CarrierConfigManager {
private void addConfig(String key, Object value, PersistableBundle configs) {
if (value instanceof String) {
configs.putString(key, (String) value);
- }
-
- if (value instanceof String[]) {
+ } else if (value instanceof String[]) {
configs.putStringArray(key, (String[]) value);
- }
-
- if (value instanceof Integer) {
+ } else if (value instanceof Integer) {
configs.putInt(key, (Integer) value);
- }
-
- if (value instanceof Long) {
+ } else if (value instanceof Long) {
configs.putLong(key, (Long) value);
- }
-
- if (value instanceof Double) {
+ } else if (value instanceof Double) {
configs.putDouble(key, (Double) value);
- }
-
- if (value instanceof Boolean) {
+ } else if (value instanceof Boolean) {
configs.putBoolean(key, (Boolean) value);
- }
-
- if (value instanceof int[]) {
+ } else if (value instanceof int[]) {
configs.putIntArray(key, (int[]) value);
- }
-
- if (value instanceof double[]) {
+ } else if (value instanceof double[]) {
configs.putDoubleArray(key, (double[]) value);
- }
-
- if (value instanceof boolean[]) {
+ } else if (value instanceof boolean[]) {
configs.putBooleanArray(key, (boolean[]) value);
- }
-
- if (value instanceof long[]) {
+ } else if (value instanceof long[]) {
configs.putLongArray(key, (long[]) value);
+ } else if (value instanceof PersistableBundle) {
+ configs.putPersistableBundle(key, (PersistableBundle) value);
}
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f3139a70eade..1b3a29d80bef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -41,6 +41,7 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.PendingIntent;
+import android.app.PropertyInvalidatedCache;
import android.app.role.RoleManager;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
@@ -361,6 +362,42 @@ public class TelephonyManager {
@GuardedBy("sCacheLock")
private static final DeathRecipient sServiceDeath = new DeathRecipient();
+ /**
+ * Cache key for a {@link PropertyInvalidatedCache} which maps from {@link PhoneAccountHandle}
+ * to subscription Id. The cache is initialized in {@code PhoneInterfaceManager}'s constructor
+ * when {@link PropertyInvalidatedCache#invalidateCache(String)} is called.
+ * The cache is cleared from {@code TelecomAccountRegistry#tearDown} when all phone accounts are
+ * removed from Telecom.
+ * @hide
+ */
+ public static final String CACHE_KEY_PHONE_ACCOUNT_TO_SUBID =
+ "cache_key.telephony.phone_account_to_subid";
+ private static final int CACHE_MAX_SIZE = 4;
+
+ /**
+ * A {@link PropertyInvalidatedCache} which lives in an app's {@link TelephonyManager} instance.
+ * Caches any queries for a mapping between {@link PhoneAccountHandle} and {@code subscription
+ * id}. The cache may be invalidated from Telephony when phone account re-registration takes
+ * place.
+ */
+ private PropertyInvalidatedCache<PhoneAccountHandle, Integer> mPhoneAccountHandleToSubIdCache =
+ new PropertyInvalidatedCache<PhoneAccountHandle, Integer>(CACHE_MAX_SIZE,
+ CACHE_KEY_PHONE_ACCOUNT_TO_SUBID) {
+ @Override
+ public Integer recompute(PhoneAccountHandle phoneAccountHandle) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getSubIdForPhoneAccountHandle(phoneAccountHandle,
+ mContext.getOpPackageName(), mContext.getAttributionTag());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ };
+
/** Enum indicating multisim variants
* DSDS - Dual SIM Dual Standby
* DSDA - Dual SIM Dual Active
@@ -11880,19 +11917,7 @@ public class TelephonyManager {
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSubscriptionId(@NonNull PhoneAccountHandle phoneAccountHandle) {
- int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- retval = service.getSubIdForPhoneAccountHandle(
- phoneAccountHandle, mContext.getOpPackageName(),
- mContext.getAttributionTag());
- }
- } catch (RemoteException ex) {
- Log.e(TAG, "getSubscriptionId RemoteException", ex);
- ex.rethrowAsRuntimeException();
- }
- return retval;
+ return mPhoneAccountHandleToSubIdCache.query(phoneAccountHandle);
}
/**
@@ -14918,7 +14943,7 @@ public class TelephonyManager {
}
ITelephony service = getITelephony();
if (service != null) {
- return service.isMvnoMatched(getSubId(), mvnoType, mvnoMatchData);
+ return service.isMvnoMatched(getSlotIndex(), mvnoType, mvnoMatchData);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "Telephony#matchesCurrentSimOperator RemoteException" + ex);
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 4e85d8926f11..a846088cec6c 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -129,17 +129,31 @@ public abstract class QualifiedNetworksService extends Service {
}
/**
- * Update the qualified networks list. Network availability provider must invoke this method
- * whenever the qualified networks changes. If this method is never invoked for certain
- * APN types, then frameworks will always use the default (i.e. cellular) data and network
- * service.
+ * Update the suggested qualified networks list. Network availability provider must invoke
+ * this method whenever the suggested qualified networks changes. If this method is never
+ * invoked for certain APN types, then frameworks uses its own logic to determine the
+ * transport to setup the data network.
+ *
+ * For example, QNS can suggest frameworks to setup IMS on IWLAN by specifying
+ * {@link ApnSetting#TYPE_IMS} with a list containing single element
+ * {@link AccessNetworkType#IWLAN}.
+ *
+ * Or if QNS consider multiple access networks are qualified for certain APN type, it can
+ * suggest frameworks by specifying the APN type with multiple elements in the list like
+ * {{@link AccessNetworkType#EUTRAN}, {@link AccessNetworkType#IWLAN}}. Frameworks will then
+ * first attempt to setup data on LTE network. If the device moves from LTE to UMTS, then
+ * frameworks can perform handover the data network to the second preferred access network
+ * if available.
+ *
+ * If the {@code qualifiedNetworkTypes} list is empty, it means QNS has no suggestion to the
+ * frameworks, and frameworks will decide the transport to setup the data network.
*
* @param apnTypes APN types of the qualified networks. This must be a bitmask combination
* of {@link ApnType}.
* @param qualifiedNetworkTypes List of network types which are qualified for data
* connection setup for {@link @apnType} in the preferred order. Each element in the list
- * is a {@link AccessNetworkType}. An empty list indicates no networks are qualified
- * for data setup.
+ * is a {@link AccessNetworkType}. Note that {@link AccessNetworkType#UNKNOWN} is not a
+ * valid input here.
*/
public final void updateQualifiedNetworkTypes(
@ApnType int apnTypes, @NonNull List<Integer> qualifiedNetworkTypes) {
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index bce210fa22a0..5bae1ad72d93 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -621,9 +621,9 @@ public class ImsMmTelManager implements RegistrationManager {
ITelephony iTelephony = getITelephony();
if (iTelephony == null) {
- throw new RuntimeException("Could not find Telephony Service.");
+ Log.w("ImsMmTelManager", "Could not find Telephony Service.");
+ return;
}
-
try {
iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e3ebb9a23950..cfd940dac750 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2142,7 +2142,7 @@ interface ITelephony {
List<RadioAccessSpecifier> getSystemSelectionChannels(int subId);
- boolean isMvnoMatched(int subId, int mvnoType, String mvnoMatchData);
+ boolean isMvnoMatched(int slotIndex, int mvnoType, String mvnoMatchData);
/**
* Enqueue a pending sms Consumer, which will answer with the user specified selection for an
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
new file mode 100644
index 000000000000..2dbf304a0f23
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+ class FixedOrientationAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+ ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
new file mode 100644
index 000000000000..2a296b7619c9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.*
+import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.*
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window layer will become visible when switching from the fixed orientation activity.
+ * To run this test: `atest FlickerTests:OpenImeWindowFromFixedOrientationAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group2
+class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val fixedOrientationApp = FixedOrientationAppHelper(instrumentation)
+ private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ fixedOrientationApp.launchViaIntent(wmHelper)
+ this.setRotation(Surface.ROTATION_90)
+ }
+ }
+ transitions {
+ imeTestApp.launchViaIntent(wmHelper)
+ }
+ teardown {
+ test {
+ fixedOrientationApp.exit(wmHelper)
+ }
+ }
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
+
+ @Postsubmit
+ @Test
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+
+ @FlakyTest(bugId = 206753786)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+
+ @Postsubmit
+ @Test
+ fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 3,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
new file mode 100644
index 000000000000..6f2edd0bb221
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.*
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import org.junit.Assume.assumeTrue
+import org.junit.Assume.assumeFalse
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window layer will be associated with the app task when going to the overview screen.
+ * To run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class OpenImeWindowToOverViewTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ imeTestApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ device.pressRecentApps()
+ waitForRecentsActivityVisible(wmHelper)
+ }
+ teardown {
+ test {
+ device.pressHome()
+ imeTestApp.exit(wmHelper)
+ }
+ }
+ }
+ }
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ fun imeWindowIsAlwaysVisible() {
+ testSpec.imeWindowIsAlwaysVisible()
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsVisibleInPortrait() {
+ assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
+ testSpec.statusBarLayerIsVisible()
+ }
+
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsInVisibleInLandscape() {
+ assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
+ testSpec.assertLayersStart {
+ this.isVisible(FlickerComponentName.STATUS_BAR)
+ }
+ testSpec.assertLayersEnd {
+ this.isInvisible(FlickerComponentName.STATUS_BAR)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun imeLayerIsVisibleAndAssociatedWithAppWidow() {
+ testSpec.assertLayersStart {
+ isVisible(FlickerComponentName.IME).visibleRegion(FlickerComponentName.IME)
+ .coversAtMost(isVisible(imeTestApp.component)
+ .visibleRegion(imeTestApp.component).region)
+ }
+ testSpec.assertLayers {
+ this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
+ val imeVisibleRegion = it.visibleRegion(FlickerComponentName.IME)
+ val appVisibleRegion = it.visibleRegion(imeTestApp.component)
+ if (imeVisibleRegion.region.isNotEmpty) {
+ it.isVisible(FlickerComponentName.IME)
+ imeVisibleRegion.coversAtMost(appVisibleRegion.region)
+ }
+ }
+ }
+ }
+
+ private fun waitForRecentsActivityVisible(
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val waitMsg = "state of Recents activity to be visible"
+ require(
+ wmHelper.waitFor(waitMsg) {
+ it.wmState.homeActivity?.let { act ->
+ it.wmState.isActivityVisible(act.name)
+ } == true ||
+ it.wmState.recentsActivity?.let { act ->
+ it.wmState.isActivityVisible(act.name)
+ } == true
+ }
+ ) { "Recents activity should be visible" }
+ wmHelper.waitForAppTransitionIdle()
+ // Ensure WindowManagerService wait until all animations have completed
+ instrumentation.uiAutomation.syncInputTransactions()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 1,
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 9e371e5e381e..739fe020d555 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -108,5 +108,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".PortraitOnlyActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:screenOrientation="portrait"
+ android:configChanges="orientation|screenSize"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 13adb681c30c..3040a09f2345 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -60,4 +60,9 @@ public class ActivityOptions {
public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".DialogThemedActivity");
+
+ public static final String PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME = "PortraitOnlyActivity";
+ public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
new file mode 100644
index 000000000000..b1876b5e5511
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class PortraitOnlyActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_simple);
+ }
+}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index 72aa38dc7e4b..103d516e5967 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -97,13 +97,8 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
setVolumeControlStream(AudioManager.STREAM_MUSIC);
- // Make sure that the service is started, so even if our activity goes down, we'll still
- // have a request for it to run.
- startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
-
- // Bind to SoundTriggerTestService.
- Intent intent = new Intent(this, SoundTriggerTestService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+ AUDIO_PERMISSIONS_REQUEST);
}
@Override
@@ -267,12 +262,16 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
public synchronized void onCaptureAudioCheckboxClicked(View v) {
// See if we have the right permissions
- if (!mService.hasMicrophonePermission()) {
- requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
- AUDIO_PERMISSIONS_REQUEST);
- return;
+ if (mService == null) {
+ Log.e(TAG, "Can't set capture audio: not bound to SoundTriggerTestService");
} else {
- mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ if (!mService.hasMicrophonePermission()) {
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+ AUDIO_PERMISSIONS_REQUEST);
+ return;
+ } else {
+ mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ }
}
}
@@ -283,8 +282,15 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
// Make sure that the check box is set to false.
mCaptureAudioCheckBox.setChecked(false);
+ } else {
+ // After granted Record_Audio permission, start and bind the service.
+ // so we can run that sound trigger capability,
+ // even if our activity goes down, we'll still have a request for it to run.
+ startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
+ // Bind to SoundTriggerTestService.
+ Intent intent = new Intent(this, SoundTriggerTestService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
- mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
}
}
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index c9c6c5cf193b..77f98e88f1eb 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -25,6 +25,8 @@ android_test {
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.uiautomator",
+ "mockito-target-minus-junit4",
+ "servicestests-utils",
"truth-prebuilt",
],
libs: [
diff --git a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
index 493f3bd22d2b..cf4965f1655d 100644
--- a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
+++ b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
@@ -41,7 +41,7 @@ abstract class BaseTrustAgentService : TrustAgentService() {
private const val TAG = "BaseTrustAgentService"
fun instance(serviceClass: KClass<out BaseTrustAgentService>): BaseTrustAgentService? {
- return instances[serviceClass]!!
+ return instances[serviceClass]
}
}
}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index af7a98c22ad1..f864fedf4e62 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -16,6 +16,7 @@
package android.trust.test
+import android.service.trust.GrantTrustResult
import android.trust.BaseTrustAgentService
import android.trust.TrustTestActivity
import android.trust.test.lib.LockStateTrackingRule
@@ -25,11 +26,13 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
+import com.android.server.testutils.mock
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
+import org.mockito.Mockito.verifyZeroInteractions
/**
* Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -66,7 +69,7 @@ class GrantAndRevokeTrustTest {
@Test
fun grantKeepsDeviceUnlocked() {
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0)
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) {}
uiDevice.sleep()
lockStateTrackingRule.assertUnlocked()
@@ -74,7 +77,7 @@ class GrantAndRevokeTrustTest {
@Test
fun grantKeepsDeviceUnlocked_untilRevoked() {
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0)
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0) {}
await()
uiDevice.sleep()
trustAgentRule.agent.revokeTrust()
@@ -82,6 +85,15 @@ class GrantAndRevokeTrustTest {
lockStateTrackingRule.assertLocked()
}
+ @Test
+ fun grantDoesNotCallBack() {
+ val callback = mock<(GrantTrustResult) -> Unit>()
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
+ await()
+
+ verifyZeroInteractions(callback)
+ }
+
companion object {
private const val TAG = "GrantAndRevokeTrustTest"
private const val GRANT_MESSAGE = "granted by test"
diff --git a/tests/TrustTests/src/android/trust/test/LockUserTest.kt b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
index a7dd41ad2e98..1194afa0123f 100644
--- a/tests/TrustTests/src/android/trust/test/LockUserTest.kt
+++ b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
@@ -57,7 +57,6 @@ class LockUserTest {
companion object {
private const val TAG = "LockUserTest"
- private fun await() = Thread.sleep(250)
}
}
diff --git a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
index 14c227b1f678..3c6d54d24291 100644
--- a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
@@ -16,16 +16,20 @@
package android.trust.test
+import android.service.trust.GrantTrustResult
+import android.service.trust.GrantTrustResult.STATUS_UNLOCKED_BY_GRANT
import android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
import android.trust.BaseTrustAgentService
import android.trust.TrustTestActivity
import android.trust.test.lib.LockStateTrackingRule
import android.trust.test.lib.ScreenLockRule
import android.trust.test.lib.TrustAgentRule
+import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -70,7 +74,8 @@ class TemporaryAndRenewableTrustTest {
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.wakeUp()
lockStateTrackingRule.assertLocked()
@@ -78,7 +83,8 @@ class TemporaryAndRenewableTrustTest {
@Test
fun grantTrustUnlockedDevice_deviceLocksOnScreenOff() {
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
@@ -86,20 +92,48 @@ class TemporaryAndRenewableTrustTest {
@Test
fun grantTrustLockedDevice_grantTrustOnLockedDeviceUnlocksDevice() {
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.wakeUp()
lockStateTrackingRule.assertUnlocked()
}
@Test
+ fun grantTrustLockedDevice_callsBackWhenUnlocked() {
+ Log.i(TAG, "Granting renewable trust while unlocked")
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ await(1000)
+
+ Log.i(TAG, "Locking device")
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+
+ Log.i(TAG, "Renewing trust and unlocking")
+ var result: GrantTrustResult? = null
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {
+ Log.i(TAG, "Callback received; status=${it.status}")
+ result = it
+ }
+ uiDevice.wakeUp()
+ lockStateTrackingRule.assertUnlocked()
+
+ assertThat(result?.status).isEqualTo(STATUS_UNLOCKED_BY_GRANT)
+ }
+
+ @Test
fun grantTrustLockedDevice_revokeTrustPreventsSubsequentUnlock() {
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
@@ -109,7 +143,8 @@ class TemporaryAndRenewableTrustTest {
uiDevice.wakeUp()
await(500)
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE)
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
lockStateTrackingRule.assertLocked()
}
diff --git a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
index f8783fbaf121..8bd8340a04b1 100644
--- a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
+++ b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
@@ -32,7 +32,7 @@ import org.junit.rules.RuleChain
import org.junit.runner.RunWith
/**
- * Test for testing the user unlock trigger.
+ * Test for the user unlock triggers.
*
* atest TrustTests:UserUnlockRequestTest
*/
@@ -53,13 +53,32 @@ class UserUnlockRequestTest {
@Test
fun reportUserRequestedUnlock_propagatesToAgent() {
val oldCount = trustAgentRule.agent.onUserRequestedUnlockCallCount
- trustManager.reportUserRequestedUnlock(userId)
+ trustManager.reportUserRequestedUnlock(userId, false)
await()
assertThat(trustAgentRule.agent.onUserRequestedUnlockCallCount)
.isEqualTo(oldCount + 1)
}
+ @Test
+ fun reportUserRequestedUnlock_propagatesToAgentWithDismissKeyguard() {
+ trustManager.reportUserRequestedUnlock(userId, true)
+ await()
+
+ assertThat(trustAgentRule.agent.lastCallDismissKeyguard)
+ .isTrue()
+ }
+
+ @Test
+ fun reportUserMayRequestUnlock_propagatesToAgent() {
+ val oldCount = trustAgentRule.agent.onUserMayRequestUnlockCallCount
+ trustManager.reportUserMayRequestUnlock(userId)
+ await()
+
+ assertThat(trustAgentRule.agent.onUserMayRequestUnlockCallCount)
+ .isEqualTo(oldCount + 1)
+ }
+
companion object {
private const val TAG = "UserUnlockRequestTest"
private fun await() = Thread.sleep(250)
@@ -69,10 +88,20 @@ class UserUnlockRequestTest {
class UserUnlockRequestTrustAgent : BaseTrustAgentService() {
var onUserRequestedUnlockCallCount: Long = 0
private set
+ var onUserMayRequestUnlockCallCount: Long = 0
+ private set
+ var lastCallDismissKeyguard: Boolean = false
+ private set
- override fun onUserRequestedUnlock() {
- Log.i(TAG, "onUserRequestedUnlock")
+ override fun onUserRequestedUnlock(dismissKeyguard: Boolean) {
+ Log.i(TAG, "onUserRequestedUnlock($dismissKeyguard)")
onUserRequestedUnlockCallCount++
+ lastCallDismissKeyguard = dismissKeyguard
+ }
+
+ override fun onUserMayRequestUnlock() {
+ Log.i(TAG, "onUserMayRequestUnlock")
+ onUserMayRequestUnlockCallCount++
}
companion object {
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 834f2122a21b..2031af2cf0c9 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -22,7 +22,6 @@ import android.content.Context
import android.util.Log
import android.view.WindowManagerGlobal
import androidx.test.core.app.ApplicationProvider.getApplicationContext
-import com.google.common.truth.Truth.assertThat
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -53,27 +52,12 @@ class LockStateTrackingRule : TestRule {
}
fun assertLocked() {
- val maxWaits = 50
- var waitCount = 0
-
- while ((lockState.locked == false) && waitCount < maxWaits) {
- Log.i(TAG, "phone still locked, wait 50ms more ($waitCount)")
- Thread.sleep(50)
- waitCount++
- }
- assertThat(lockState.locked).isTrue()
+ wait("un-locked per TrustListener") { lockState.locked == true }
+ wait("keyguard lock") { windowManager.isKeyguardLocked }
}
fun assertUnlocked() {
- val maxWaits = 50
- var waitCount = 0
-
- while ((lockState.locked == true) && waitCount < maxWaits) {
- Log.i(TAG, "phone still unlocked, wait 50ms more ($waitCount)")
- Thread.sleep(50)
- waitCount++
- }
- assertThat(lockState.locked).isFalse()
+ wait("locked per TrustListener") { lockState.locked == false }
}
inner class Listener : TrustListener {
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
index 006525d857ac..7eb8157d4b24 100644
--- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -42,7 +42,7 @@ class ScreenLockRule : TestRule {
override fun apply(base: Statement, description: Description) = object : Statement() {
override fun evaluate() {
verifyNoScreenLockAlreadySet()
- verifyKeyguardDismissed()
+ dismissKeyguard()
setScreenLock()
setLockOnPowerButton()
@@ -51,7 +51,7 @@ class ScreenLockRule : TestRule {
} finally {
removeScreenLock()
revertLockOnPowerButton()
- verifyKeyguardDismissed()
+ dismissKeyguard()
}
}
}
@@ -62,19 +62,21 @@ class ScreenLockRule : TestRule {
.isFalse()
}
- private fun verifyKeyguardDismissed() {
- val maxWaits = 30
- var waitCount = 0
-
- while (windowManager.isKeyguardLocked && waitCount < maxWaits) {
- Log.i(TAG, "Keyguard still showing; attempting to dismiss and wait 50ms ($waitCount)")
+ fun dismissKeyguard() {
+ wait("keyguard dismissed") { count ->
windowManager.dismissKeyguard(null, null)
- Thread.sleep(50)
- waitCount++
+
+ // Sometimes, bouncer gets shown due to a race, so we have to put display to sleep
+ // and wake it back up to get it to go away
+ if (count >= 10 && count % 5 == 0) {
+ Log.i(TAG, "Escalation: attempting screen off/on to get rid of bouncer")
+ uiDevice.sleep()
+ Thread.sleep(250)
+ uiDevice.wakeUp()
+ }
+
+ !windowManager.isKeyguardLocked
}
- assertWithMessage("Keyguard should be unlocked")
- .that(windowManager.isKeyguardLocked)
- .isFalse()
}
private fun setScreenLock() {
@@ -83,9 +85,7 @@ class ScreenLockRule : TestRule {
LockscreenCredential.createNone(),
context.userId
)
- assertWithMessage("Screen Lock should now be set")
- .that(lockPatternUtils.isSecure(context.userId))
- .isTrue()
+ wait("screen lock set") { lockPatternUtils.isSecure(context.userId) }
Log.i(TAG, "Device PIN set to $PIN")
}
@@ -99,21 +99,16 @@ class ScreenLockRule : TestRule {
LockscreenCredential.createNone(),
LockscreenCredential.createPin(PIN),
context.userId)
- Thread.sleep(100)
+ Log.i(TAG, "Removing screen lock")
assertWithMessage("Lock screen credential should be unset")
.that(lockCredentialUnset)
.isTrue()
lockPatternUtils.setLockScreenDisabled(true, context.userId)
- Thread.sleep(100)
- assertWithMessage("Lockscreen needs to be disabled")
- .that(lockPatternUtils.isLockScreenDisabled(context.userId))
- .isTrue()
-
- // this is here because somehow it helps the keyguard not get stuck
- uiDevice.sleep()
- Thread.sleep(500) // delay added to avoid initiating camera by double clicking power
- uiDevice.wakeUp()
+ wait("screen lock un-set") {
+ lockPatternUtils.isLockScreenDisabled(context.userId)
+ }
+ wait("screen lock insecure") { !lockPatternUtils.isSecure(context.userId) }
}
private fun revertLockOnPowerButton() {
diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
index 2a9e00276475..18bc029b6845 100644
--- a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
@@ -50,7 +50,6 @@ class TrustAgentRule<T : BaseTrustAgentService>(
verifyTrustServiceRunning()
unlockDeviceWithCredential()
enableTrustAgent()
- waitForEnablement()
try {
verifyAgentIsRunning()
@@ -80,15 +79,10 @@ class TrustAgentRule<T : BaseTrustAgentService>(
lockPatternUtils.setEnabledTrustAgents(agents, userId)
}
- private fun waitForEnablement() {
- Log.d(TAG, "Waiting for $WAIT_TIME ms")
- Thread.sleep(WAIT_TIME)
- Log.d(TAG, "Done waiting")
- }
-
private fun verifyAgentIsRunning() {
- assertWithMessage("${serviceClass.simpleName} should be running")
- .that(BaseTrustAgentService.instance(serviceClass)).isNotNull()
+ wait("${serviceClass.simpleName} to be running") {
+ BaseTrustAgentService.instance(serviceClass) != null
+ }
}
private fun disableTrustAgent() {
@@ -112,6 +106,5 @@ class TrustAgentRule<T : BaseTrustAgentService>(
TrustAgentRule(T::class)
private const val TAG = "TrustAgentRule"
- private val WAIT_TIME = 1000L
}
}
diff --git a/tests/TrustTests/src/android/trust/test/lib/utils.kt b/tests/TrustTests/src/android/trust/test/lib/utils.kt
new file mode 100644
index 000000000000..78140abec210
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/utils.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.trust.test.lib
+
+import android.util.Log
+import com.google.common.truth.Truth.assertWithMessage
+
+private const val TAG = "TrustTestUtils"
+
+/**
+ * Waits for [conditionFunction] to be true with a failed assertion if it is not after [maxWait]
+ * ms.
+ *
+ * The condition function can perform additional logic (for example, logging or attempting to make
+ * the condition become true).
+ *
+ * @param conditionFunction function which takes the attempt count & returns whether the condition
+ * is met
+ */
+internal fun wait(
+ description: String? = null,
+ maxWait: Long = 1500L,
+ rate: Long = 50L,
+ conditionFunction: (count: Int) -> Boolean
+) {
+ var waited = 0L
+ var count = 0
+ while (!conditionFunction.invoke(count)) {
+ assertWithMessage("Condition exceeded maximum wait time of $maxWait ms: $description")
+ .that(waited <= maxWait)
+ .isTrue()
+ waited += rate
+ count++
+ Log.i(TAG, "Waiting for $description ($waited/$maxWait) #$count")
+ Thread.sleep(rate)
+ }
+}
diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp
index 3e1297190622..6efd1f64d8fe 100644
--- a/tools/locked_region_code_injection/Android.bp
+++ b/tools/locked_region_code_injection/Android.bp
@@ -12,10 +12,10 @@ java_binary_host {
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-7.0",
- "asm-commons-7.0",
- "asm-tree-7.0",
- "asm-analysis-7.0",
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
"guava-21.0",
],
}
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index ec2bffdfaf57..6ebacd8a0b14 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@ java_binary_host {
"src/**/*.kt",
],
static_libs: [
- "asm-7.0",
+ "asm-9.2",
],
}
diff --git a/tools/traceinjection/Android.bp b/tools/traceinjection/Android.bp
index 1395c5f2e635..39d1b1c2defd 100644
--- a/tools/traceinjection/Android.bp
+++ b/tools/traceinjection/Android.bp
@@ -12,10 +12,10 @@ java_binary_host {
manifest: "manifest.txt",
srcs: ["src/**/*.java"],
static_libs: [
- "asm-7.0",
- "asm-commons-7.0",
- "asm-tree-7.0",
- "asm-analysis-7.0",
+ "asm-9.2",
+ "asm-commons-9.2",
+ "asm-tree-9.2",
+ "asm-analysis-9.2",
"guava-21.0",
],
}